diff --git a/BUILD.gn b/BUILD.gn
index d15e139..7a5ad82e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1293,6 +1293,7 @@
     testonly = true
     data_deps = [
       "chrome/browser/resources:closure_compile",
+      "components/sync/driver/resources:closure_compile",
       "components/ukm/debug:closure_compile",
       "content/browser/resources:closure_compile",
       "mojo/public/tools/bindings/generators/js_templates/lite/test:closure_compile",
diff --git a/DEPS b/DEPS
index 1697d07..bcd6ca69 100644
--- a/DEPS
+++ b/DEPS
@@ -144,11 +144,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '84bcd0c3ae08f2add3a3999cff784fe907b03c45',
+  'skia_revision': '829144cc76fe7c4e80cc7d6498ff895a0bddbb62',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '109b30b38f8365b80ff4b7c563d19264789d6865',
+  'v8_revision': '4035531228d69a8e3ec475ef75b51db302e70473',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -156,7 +156,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'dd3de6f095c1a0e8b7c4fff579541287ab2f4fe6',
+  'angle_revision': '8bb46c5b9ffc89e3d5eeddcd990fd69bb4cbeecd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -164,7 +164,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '0a30c29ba84639c7cc631656121a78264ae78bb0',
+  'pdfium_revision': '0208b0ca4d258aa2ac8a938725d1afaa591c1a92',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -207,7 +207,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '282f4dfee653d0ecb74a111a57b501617f6eb99f',
+  'catapult_revision': '5f7b2c2ad2f211c00516d4a44787c754f3acf52c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -283,7 +283,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '0b5bfaebe8ba794946fe335423c4013f2743ddee',
+  'quiche_revision': '89b9af539b5877093cdf7ba22cbff9354b6bf723',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -808,7 +808,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ec869c11c79de47fe0b31a17ad597337fb653117',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1a62d1623e3bfe81f9f26030e700e82d34a72d81',
       'condition': 'checkout_linux',
   },
 
@@ -1206,7 +1206,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '5f008e96a17fc8dfde695e50429222d857414b93',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '67440068b934743e5e84909f0c29089ab610a5d3',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1415,7 +1415,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@59b2d77a1e398b1154bbcb0e022683d57ca6f5a2',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6d863b5a5eadaacda2ad565d40381c2ab5efc334',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/style/ash_color_provider.cc b/ash/style/ash_color_provider.cc
index 963c77e..891d2d9 100644
--- a/ash/style/ash_color_provider.cc
+++ b/ash/style/ash_color_provider.cc
@@ -9,11 +9,16 @@
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/color_utils.h"
 
 namespace ash {
 
 namespace {
 
+// Opacity of the light/dark ink ripple.
+constexpr float kLightInkRippleOpacity = 0.08f;
+constexpr float kDarkInkRippleOpacity = 0.06f;
+
 // Gets the color mode value from feature flag "--ash-color-mode".
 AshColorProvider::AshColorMode GetColorModeFromCommandLine() {
   const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
@@ -98,6 +103,15 @@
   return SelectColorOnMode(light_color, dark_color);
 }
 
+AshColorProvider::RippleAttributes AshColorProvider::GetRippleAttributes(
+    SkColor bg_color) const {
+  const SkColor base_color = color_utils::GetColorWithMaxContrast(bg_color);
+  const float opacity = color_utils::IsDark(base_color)
+                            ? kDarkInkRippleOpacity
+                            : kLightInkRippleOpacity;
+  return RippleAttributes(base_color, opacity, opacity);
+}
+
 SkColor AshColorProvider::SelectColorOnMode(SkColor light_color,
                                             SkColor dark_color) const {
   if (color_mode_ == AshColorMode::kLight)
diff --git a/ash/style/ash_color_provider.h b/ash/style/ash_color_provider.h
index 3803330..267e4bf9 100644
--- a/ash/style/ash_color_provider.h
+++ b/ash/style/ash_color_provider.h
@@ -62,6 +62,20 @@
     kFocusRing,
   };
 
+  // Attributes of ripple, includes the base color, opacity of inkdrop and
+  // highlight.
+  struct RippleAttributes {
+    RippleAttributes(SkColor color,
+                     float opacity_of_inkdrop,
+                     float opacity_of_highlight)
+        : base_color(color),
+          inkdrop_opacity(opacity_of_inkdrop),
+          highlight_opacity(opacity_of_highlight) {}
+    const SkColor base_color;
+    const float inkdrop_opacity;
+    const float highlight_opacity;
+  };
+
   AshColorProvider();
   ~AshColorProvider();
 
@@ -70,6 +84,10 @@
   SkColor GetBaseLayerColor(BaseLayerType type) const;
   SkColor GetControlsLayerColor(ControlsLayerType type) const;
 
+  // Gets the attributes of ripple on |bg_color|. |bg_color| is the background
+  // color of the UI element that wants to show inkdrop.
+  RippleAttributes GetRippleAttributes(SkColor bg_color) const;
+
   AshColorMode color_mode() const { return color_mode_; }
 
  private:
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 289cff1..e7ba024 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1497,6 +1497,8 @@
       "files/file_posix.cc",
       "files/file_util_posix.cc",
       "files/memory_mapped_file_posix.cc",
+      "fuchsia/default_context.cc",
+      "fuchsia/default_context.h",
       "fuchsia/default_job.cc",
       "fuchsia/default_job.h",
       "fuchsia/file_utils.cc",
@@ -1567,6 +1569,7 @@
       "//third_party/fuchsia-sdk/sdk:fdio",
       "//third_party/fuchsia-sdk/sdk:fidl_cpp",
       "//third_party/fuchsia-sdk/sdk:io",
+      "//third_party/fuchsia-sdk/sdk:sys_cpp",
       "//third_party/fuchsia-sdk/sdk:zx",
     ]
 
@@ -1575,9 +1578,9 @@
       "//third_party/fuchsia-sdk/sdk:async_loop_cpp",
       "//third_party/fuchsia-sdk/sdk:deprecatedtimezone",
       "//third_party/fuchsia-sdk/sdk:fidl",
-      "//third_party/fuchsia-sdk/sdk:svc",
       "//third_party/fuchsia-sdk/sdk:sys",
       "//third_party/fuchsia-sdk/sdk:syslog",
+      "//third_party/fuchsia-sdk/sdk:vfs_cpp",
     ]
   }
 
@@ -2984,7 +2987,6 @@
       "fuchsia/scoped_service_binding_unittest.cc",
       "fuchsia/service_directory_test_base.cc",
       "fuchsia/service_directory_test_base.h",
-      "fuchsia/service_directory_unittest.cc",
       "fuchsia/service_provider_impl_unittest.cc",
       "message_loop/message_loop_io_posix_unittest.cc",
       "posix/file_descriptor_shuffle_unittest.cc",
@@ -3237,7 +3239,6 @@
       "android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
       "android/java/src/org/chromium/base/library_loader/Linker.java",
       "android/java/src/org/chromium/base/library_loader/LoaderErrors.java",
-      "android/java/src/org/chromium/base/library_loader/ModernLinker.java",
       "android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java",
       "android/java/src/org/chromium/base/library_loader/ProcessInitException.java",
       "android/java/src/org/chromium/base/metrics/CachedMetrics.java",
diff --git a/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java b/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
index dd52e942..e05fcf5 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
@@ -4,10 +4,12 @@
 
 package org.chromium.base.library_loader;
 
+import android.annotation.SuppressLint;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.support.annotation.Nullable;
 
+import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.annotations.JniIgnoreNatives;
 
@@ -35,13 +37,51 @@
     // Log tag for this class.
     private static final String TAG = "LegacyLinker";
 
+    // Becomes true after linker initialization.
+    private boolean mInitialized;
+
+    // Set to true if this runs in the browser process. Disabled by initServiceProcess().
+    private boolean mInBrowserProcess = true;
+
+    // Becomes true to indicate this process needs to wait for a shared RELRO in
+    // finishLibraryLoad().
+    private boolean mWaitForSharedRelros;
+
+    // The map of all RELRO sections either created or used in this process.
+    private Bundle mSharedRelros;
+
+    // Current common random base load address. A value of -1 indicates not yet initialized.
+    private long mBaseLoadAddress = -1;
+
+    // Current fixed-location load address for the next library called by loadLibrary().
+    // A value of -1 indicates not yet initialized.
+    private long mCurrentLoadAddress = -1;
+
+    // The map of libraries that are currently loaded in this process.
+    private HashMap<String, LibInfo> mLoadedLibraries;
+
     LegacyLinker() {}
 
+    // Used internally to initialize the linker's data. Assumes lock is held.
+    // Loads JNI, and sets mMemoryDeviceConfig and mBrowserUsesSharedRelro.
+    @GuardedBy("sLock")
+    private void ensureInitializedLocked() {
+        assert Thread.holdsLock(sLock);
+
+        if (mInitialized) return;
+
+        // On first call, load libchromium_android_linker.so. Cannot be done in the
+        // constructor because instantiation occurs on the UI thread.
+        loadLinkerJniLibrary();
+
+        mInitialized = true;
+    }
+
     /**
      * Call this method just before loading any native shared libraries in this process.
      */
     @Override
-    void prepareLibraryLoad(@Nullable String apkFilePath) {
+    public void prepareLibraryLoad(@Nullable String apkFilePath) {
         if (DEBUG) Log.i(TAG, "prepareLibraryLoad() called");
         synchronized (sLock) {
             ensureInitializedLocked();
@@ -63,7 +103,7 @@
      * received, i.e. when another thread calls useSharedRelros().
      */
     @Override
-    void finishLibraryLoad() {
+    public void finishLibraryLoad() {
         if (DEBUG) Log.i(TAG, "finishLibraryLoad() called");
 
         synchronized (sLock) {
@@ -81,30 +121,31 @@
                 if (mInBrowserProcess) {
                     // Create new Bundle containing RELRO section information
                     // for all loaded libraries. Make it available to getSharedRelros().
-                    mSharedRelrosBundle = createBundleFromLibInfoMap(mLoadedLibraries);
+                    mSharedRelros = createBundleFromLibInfoMap(mLoadedLibraries);
                     if (DEBUG) {
                         Log.i(TAG, "Shared RELRO created");
-                        dumpBundle(mSharedRelrosBundle);
+                        dumpBundle(mSharedRelros);
                     }
 
-                    useSharedRelrosLocked(mSharedRelrosBundle);
+                    useSharedRelrosLocked(mSharedRelros);
                 }
 
                 if (mWaitForSharedRelros) {
                     assert !mInBrowserProcess;
 
                     // Wait until the shared relro bundle is received from useSharedRelros().
-                    while (mSharedRelrosBundle == null) {
+                    while (mSharedRelros == null) {
                         try {
                             sLock.wait();
                         } catch (InterruptedException ie) {
-                            // Continue waiting even if we were just interrupted.
+                            // Restore the thread's interrupt status.
+                            Thread.currentThread().interrupt();
                         }
                     }
-                    useSharedRelrosLocked(mSharedRelrosBundle);
+                    useSharedRelrosLocked(mSharedRelros);
                     // Clear the Bundle to ensure its file descriptor references can't be reused.
-                    mSharedRelrosBundle.clear();
-                    mSharedRelrosBundle = null;
+                    mSharedRelros.clear();
+                    mSharedRelros = null;
                 }
             }
 
@@ -144,7 +185,7 @@
         synchronized (sLock) {
             // Note that in certain cases, this can be called before
             // initServiceProcess() in service processes.
-            mSharedRelrosBundle = clonedBundle;
+            mSharedRelros = clonedBundle;
             // Tell any listener blocked in finishLibraryLoad() about it.
             sLock.notifyAll();
         }
@@ -167,8 +208,44 @@
             }
 
             // Return the Bundle created in finishLibraryLoad().
-            if (DEBUG) Log.i(TAG, "... returning %s", mSharedRelrosBundle);
-            return mSharedRelrosBundle;
+            if (DEBUG) Log.i(TAG, "... returning %s", mSharedRelros);
+            return mSharedRelros;
+        }
+    }
+
+    /**
+     * Call this method before loading any libraries to indicate that this
+     * process shall neither create or reuse shared RELRO sections.
+     */
+    @Override
+    public void disableSharedRelros() {
+        if (DEBUG) Log.i(TAG, "disableSharedRelros() called");
+        synchronized (sLock) {
+            ensureInitializedLocked();
+            mInBrowserProcess = false;
+            mWaitForSharedRelros = false;
+        }
+    }
+
+    /**
+     * Call this method before loading any libraries to indicate that this
+     * process is ready to reuse shared RELRO sections from another one.
+     * Typically used when starting service processes.
+     *
+     * @param baseLoadAddress the base library load address to use.
+     */
+    @Override
+    public void initServiceProcess(long baseLoadAddress) {
+        if (DEBUG) {
+            Log.i(TAG,
+                    String.format(Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress));
+        }
+        synchronized (sLock) {
+            ensureInitializedLocked();
+            mInBrowserProcess = false;
+            mWaitForSharedRelros = true;
+            mBaseLoadAddress = baseLoadAddress;
+            mCurrentLoadAddress = baseLoadAddress;
         }
     }
 
@@ -190,12 +267,30 @@
             }
 
             setupBaseLoadAddressLocked();
-            if (DEBUG) Log.i(TAG, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress);
-
+            if (DEBUG) {
+                Log.i(TAG,
+                        String.format(
+                                Locale.US, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress));
+            }
             return mBaseLoadAddress;
         }
     }
 
+    // Used internally to lazily setup the common random base load address.
+    @GuardedBy("sLock")
+    private void setupBaseLoadAddressLocked() {
+        if (mBaseLoadAddress == -1) {
+            mBaseLoadAddress = getRandomBaseLoadAddress();
+            mCurrentLoadAddress = mBaseLoadAddress;
+            if (mBaseLoadAddress == 0) {
+                // If the random address is 0 there are issues with finding enough
+                // free address space, so disable RELRO shared / fixed load addresses.
+                Log.w(TAG, "Disabling shared RELROs due address space pressure");
+                mWaitForSharedRelros = false;
+            }
+        }
+    }
+
     // Used for debugging only.
     private void dumpBundle(Bundle bundle) {
         if (DEBUG) Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle);
@@ -245,6 +340,27 @@
     }
 
     /**
+     * Load the Linker JNI library. Throws UnsatisfiedLinkError on error.
+     */
+    @SuppressLint({"UnsafeDynamicallyLoadedCode"})
+    protected static void loadLinkerJniLibrary() {
+        LibraryLoader.setEnvForNative();
+        if (DEBUG) {
+            String libName = "lib" + LINKER_JNI_LIBRARY + ".so";
+            Log.i(TAG, "Loading %s", libName);
+        }
+        try {
+            System.loadLibrary(LINKER_JNI_LIBRARY);
+        } catch (UnsatisfiedLinkError e) {
+            if (LibraryLoader.PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
+                System.load(LibraryLoader.getExtractedLibraryPath(
+                        ContextUtils.getApplicationContext().getApplicationInfo(),
+                        LINKER_JNI_LIBRARY));
+            }
+        }
+    }
+
+    /**
      * Implements loading a native shared library with the Chromium linker.
      *
      * Load a native shared library with the Chromium linker. If the zip file
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index dd22909f..ccb1e46 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -118,6 +118,31 @@
     private long mLibraryLoadTimeMs;
 
     /**
+     * Call this method to determine if this chromium project must
+     * use this linker. If not, System.loadLibrary() should be used to load
+     * libraries instead.
+     */
+    public static boolean useCrazyLinker() {
+        // A non-monochrome APK (such as ChromePublic.apk) can be installed on N+ in these
+        // circumstances:
+        // * installing APK manually
+        // * after OTA from M to N
+        // * side-installing Chrome (possibly from another release channel)
+        // * Play Store bugs leading to incorrect APK flavor being installed
+        // * installing other Chromium-based browsers
+        //
+        // For Chrome builds regularly shipped to users on N+, the system linker (or the Android
+        // Framework) provides the necessary functionality to load without crazylinker. The
+        // crazylinker is risky to auto-enable on newer Android releases, as it may interfere with
+        // regular library loading. See http://crbug.com/980304 as example.
+        if (Build.VERSION.SDK_INT >= VERSION_CODES.N) return false;
+
+        // The auto-generated NativeLibraries.sUseLinker variable will be true if the
+        // build has not explicitly disabled Linker features.
+        return NativeLibraries.sUseLinker;
+    }
+
+    /**
      * Call this method to determine if the chromium project must load the library
      * directly from a zip file.
      */
@@ -186,15 +211,16 @@
      */
     public void preloadNowOverrideApplicationContext(Context appContext) {
         synchronized (mLock) {
-            if (useChromiumLinker()) return;
-            preloadAlreadyLocked(appContext.getApplicationInfo());
+            if (!useCrazyLinker()) {
+                preloadAlreadyLocked(appContext.getApplicationInfo());
+            }
         }
     }
 
     private void preloadAlreadyLocked(ApplicationInfo appInfo) {
         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
             // Preloader uses system linker, we shouldn't preload if Chromium linker is used.
-            assert !useChromiumLinker();
+            assert !useCrazyLinker();
             if (mLibraryPreloader != null && !mLibraryPreloaderCalled) {
                 mLibraryPreloader.loadLibrary(appInfo);
                 mLibraryPreloaderCalled = true;
@@ -323,7 +349,7 @@
 
                 long startTime = SystemClock.uptimeMillis();
 
-                if (useChromiumLinker() && !inZygote) {
+                if (useCrazyLinker() && !inZygote) {
                     // Load libraries using the Chromium linker.
                     Linker linker = Linker.getInstance();
 
@@ -554,7 +580,7 @@
     // Called after all native initializations are complete.
     public void onBrowserNativeInitializationComplete() {
         synchronized (mLock) {
-            if (useChromiumLinker()) {
+            if (useCrazyLinker()) {
                 RecordHistogram.recordTimesHistogram(
                         "ChromiumAndroidLinker.BrowserLoadTime", mLibraryLoadTimeMs);
             }
@@ -567,22 +593,13 @@
     // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
     public void registerRendererProcessHistogram() {
         synchronized (mLock) {
-            if (useChromiumLinker()) {
+            if (useCrazyLinker()) {
                 nativeRecordRendererLibraryLoadTime(mLibraryLoadTimeMs);
             }
         }
     }
 
     /**
-     * Call this method to determine if this chromium project must
-     * use this linker. If not, System.loadLibrary() should be used to load
-     * libraries instead.
-     */
-    public static boolean useChromiumLinker() {
-        return NativeLibraries.sUseLinker;
-    }
-
-    /**
      * Override the library loader (normally with a mock) for testing.
      * @param loader the mock library loader.
      */
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 09d56e6..c655f723 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -4,15 +4,12 @@
 
 package org.chromium.base.library_loader;
 
-import android.annotation.SuppressLint;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.support.annotation.Nullable;
 
-import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
 import org.chromium.base.annotations.AccessedByNative;
@@ -22,8 +19,6 @@
 import java.util.Locale;
 import java.util.Map;
 
-import javax.annotation.concurrent.GuardedBy;
-
 /*
  * Technical note:
  *
@@ -36,8 +31,9 @@
  * the content of their RELRO section (which includes C++ vtables or any
  * constants that contain pointers) will be largely identical [1].
  *
- * By default, the RELRO section is backed by private RAM in each process, which is still
- * significant on mobile (e.g. ~2 MB / process on Chrome 77 ARM, more on ARM64).
+ * By default, the RELRO section is backed by private RAM in each process,
+ * which is still significant on mobile (e.g. 1.28 MB / process on Chrome 30 for
+ * Android).
  *
  * However, it is possible to save RAM by creating a shared memory region,
  * copy the RELRO content into it, then have each process swap its private,
@@ -136,7 +132,7 @@
 @JniIgnoreNatives
 public abstract class Linker {
     // Log tag for this class.
-    private static final String TAG = "Linker";
+    private static final String TAG = "LibraryLoader";
 
     // Name of the library that contains our JNI code.
     protected static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
@@ -157,124 +153,37 @@
     // ensure that we don't try to load outside the area originally requested.
     protected static final int ADDRESS_SPACE_RESERVATION = 192 * 1024 * 1024;
 
-    // Constants used to indicate a given Linker implementation, for testing.
-    //   LEGACY       -> Always uses the LegacyLinker implementation.
-    //   MODERN       -> Always uses the ModernLinker implementation.
-    // NOTE: These names are known and expected by the Linker test scripts.
-    public static final int LINKER_IMPLEMENTATION_LEGACY = 1;
-    public static final int LINKER_IMPLEMENTATION_MODERN = 2;
-
     // Singleton.
     protected static final Object sLock = new Object();
     private static Linker sSingleton;
 
-    // Variables below are used in derived classes.
-    // Becomes true after linker initialization.
-    protected boolean mInitialized;
-
-    // Becomes true to indicate this process needs to wait for a shared RELRO in LibraryLoad().
-    protected boolean mWaitForSharedRelros;
-
-    // Cached Bundle representation of the RELRO sections map for transfer across processes.
-    protected Bundle mSharedRelrosBundle;
-
-    // Set to true if this runs in the browser process. Disabled by initServiceProcess().
-    protected boolean mInBrowserProcess = true;
-
-    // Current common random base load address. A value of -1 indicates not yet initialized.
-    protected long mBaseLoadAddress = -1;
-
-    // Current fixed-location load address for the next library called by loadLibrary().
-    // Initialized to mBaseLoadAddress in prepareLibraryLoad(), and then adjusted as each
-    // library is loaded by loadLibrary().
-    protected long mCurrentLoadAddress = -1;
-
-    // The map of libraries that are currently loaded in this process.
-    protected HashMap<String, LibInfo> mLoadedLibraries;
-
     // Protected singleton constructor.
     protected Linker() {}
 
     /**
-     * Get singleton instance. Returns either a LegacyLinker or a ModernLinker.
+     * Get singleton instance. Returns a LegacyLinker.
      *
-     * Returns a ModernLinker if running on Android M or later, otherwise returns
-     * a LegacyLinker.
+     * On N+ Monochrome is selected by Play Store. With Monochrome this code is not used, instead
+     * Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView fails
+     * to provide the library, the system linker is used as a fallback.
      *
-     * ModernLinker requires OS features from Android M and later: a system linker
-     * that handles packed relocations and load from APK, and |android_dlopen_ext()|
-     * for shared RELRO support. It cannot run on Android releases earlier than M.
-     *
-     * LegacyLinker runs on all Android releases but it is slower and more complex
-     * than ModernLinker. We still use it on M as it avoids writing the relocation to disk.
-     *
-     * On N, O and P Monochrome is selected by Play Store. With Monochrome this code is not used,
-     * instead Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView
-     * fails to provide the library, the system linker is used as a fallback.
-     *
-     * LegacyLinker can run on all Android releases, but is unused on P+ as it may cause issues.
-     * LegacyLinker is preferred on N- because it does not write the shared RELRO to disk at
+     * LegacyLinker runs on all Android releases, but is incompatible with GVR library on N+.
+     * LegacyLinker is preferred on M- because it does not write the shared RELRO to disk at
      * almost every cold startup.
      *
-     * Finally, ModernLinker is used on Android N+ when installing Chrome{,Modern}.apk, which is not
-     * a configuration shipped through the play store, but kept here temporarily to ease testing.
-     *
      * @return the Linker implementation instance.
      */
     public static Linker getInstance() {
-        // A non-monochrome APK (such as ChromePublic.apk) can be installed on N+ in these
-        // circumstances:
-        // * installing APK manually
-        // * after OTA from M to N
-        // * side-installing Chrome (possibly from another release channel)
-        // * Play Store bugs leading to incorrect APK flavor being installed
-        // * installing other Chromium-based browsers
-        //
-        // For Chrome builds regularly shipped to users on N+, the system linker (or the Android
-        // Framework) provides the necessary functionality to load without crazylinker. The
-        // LegacyLinker is risky to auto-enable on newer Android releases, as it may interfere with
-        // regular library loading. See http://crbug.com/980304 as example.
-        //
-        // This is only called if LibraryLoader.useChromiumLinker() returns true, meaning this is
-        // either Chrome{,Modern} or the linker tests.
-        //
-        // TODO(lizeb): Also check that this is a local build to avoid shipping ModernLinker
-        // accidentally.
         synchronized (sLock) {
             if (sSingleton == null) {
-                // With incremental install, it's important to fall back to the "normal"
-                // library loading path in order for the libraries to be found.
-                String appClass =
-                        ContextUtils.getApplicationContext().getApplicationInfo().className;
-                boolean isIncrementalInstall =
-                        appClass != null && appClass.contains("incrementalinstall");
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !isIncrementalInstall) {
-                    // This is not hit for shipping versions, as the Chrome flavor on N+ is
-                    // MonoChrome, and this requires both Chrome.apk and N+.
-                    sSingleton = new ModernLinker();
-                } else {
-                    sSingleton = new LegacyLinker();
-                }
-                Log.i(TAG, "Using linker: %s", sSingleton.getClass().getName());
+                sSingleton = new LegacyLinker();
+                Log.i(TAG, "Using linker: LegacyLinker");
             }
             return sSingleton;
         }
     }
 
     /**
-     * Call this method before loading any libraries to indicate that this
-     * process shall neither create or reuse shared RELRO sections.
-     */
-    public void disableSharedRelros() {
-        if (DEBUG) Log.i(TAG, "disableSharedRelros() called");
-        synchronized (sLock) {
-            ensureInitializedLocked();
-            mInBrowserProcess = false;
-            mWaitForSharedRelros = false;
-        }
-    }
-
-    /**
      * Check that native library linker tests are enabled.
      * If not enabled, calls to testing functions will fail with an assertion
      * error.
@@ -286,25 +195,15 @@
     }
 
     /**
-     * Get Linker implementation type.
-     * For testing.
-     *
-     * @return LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN
+     * Assert NativeLibraries.sEnableLinkerTests is true.
+     * Hard assertion that we are in a testing context. Cannot be disabled. The
+     * test methods in this module permit injection of runnable code by class
+     * name. To protect against both malicious and accidental use of these
+     * methods, we ensure that NativeLibraries.sEnableLinkerTests is true when
+     * any is called.
      */
-    public final int getImplementationForTesting() {
-        // Sanity check. This method may only be called during tests.
-        assert NativeLibraries.sEnableLinkerTests;
-
-        synchronized (sLock) {
-            assert sSingleton == this;
-
-            if (sSingleton instanceof ModernLinker) {
-                return LINKER_IMPLEMENTATION_MODERN;
-            } else if (sSingleton instanceof LegacyLinker) {
-                return LINKER_IMPLEMENTATION_LEGACY;
-            }
-            throw new AssertionError("Invalid linker: " + sSingleton.getClass().getName());
-        }
+    private static void assertLinkerTestsAreEnabled() {
+        assert NativeLibraries.sEnableLinkerTests : "Testing method called in non-testing context";
     }
 
     /**
@@ -332,7 +231,8 @@
      * the TestRunner set by calling setTestRunnerClassNameForTesting() previously.
      */
     public final String getTestRunnerClassNameForTesting() {
-        assert NativeLibraries.sEnableLinkerTests;
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
 
         synchronized (sLock) {
             return mTestRunnerClassName;
@@ -340,29 +240,20 @@
     }
 
     /**
-     * Set up the Linker for a test.
-     * Convenience function that calls setImplementationForTesting() to force an
-     * implementation, and then setTestRunnerClassNameForTesting() to set the test
-     * class name.
+     * Sets the test class name.
      *
-     * On first call, instantiates a Linker of the requested type and sets its test
-     * runner class name. On subsequent calls, checks that the singleton produced by
-     * the first call matches the requested type and test runner class name.
+     * On the first call, instantiates a Linker and sets its test runner class name. On subsequent
+     * calls, checks that the singleton produced by the first call matches the test runner class
+     * name.
      */
-    public static final void setupForTesting(int type, String testRunnerClassName) {
-        assert NativeLibraries.sEnableLinkerTests;
-        assert type == LINKER_IMPLEMENTATION_LEGACY || type == LINKER_IMPLEMENTATION_MODERN;
-
-        if (DEBUG) Log.i(TAG, "setupForTesting(%d, %s) called", type, testRunnerClassName);
+    public static final void setupForTesting(String testRunnerClassName) {
+        if (DEBUG) {
+            Log.i(TAG, "setupForTesting(" + testRunnerClassName + ") called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
 
         synchronized (sLock) {
-            assert sSingleton == null;
-            if (type == LINKER_IMPLEMENTATION_MODERN) {
-                sSingleton = new ModernLinker();
-            } else if (type == LINKER_IMPLEMENTATION_LEGACY) {
-                sSingleton = new LegacyLinker();
-            }
-            Log.i(TAG, "Forced linker: %s", sSingleton.getClass().getName());
             Linker.getInstance().mTestRunnerClassName = testRunnerClassName;
         }
     }
@@ -375,8 +266,11 @@
      * @param inBrowserProcess true if in the browser process
      */
     protected final void runTestRunnerClassForTesting(boolean inBrowserProcess) {
-        assert NativeLibraries.sEnableLinkerTests;
-        if (DEBUG) Log.i(TAG, "runTestRunnerClassForTesting called");
+        if (DEBUG) {
+            Log.i(TAG, "runTestRunnerClassForTesting called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
 
         synchronized (sLock) {
             if (mTestRunnerClassName == null) {
@@ -509,21 +403,18 @@
 
     /**
      * Call this method before loading any libraries to indicate that this
+     * process shall neither create or reuse shared RELRO sections.
+     */
+    public abstract void disableSharedRelros();
+
+    /**
+     * Call this method before loading any libraries to indicate that this
      * process is ready to reuse shared RELRO sections from another one.
      * Typically used when starting service processes.
      *
      * @param baseLoadAddress the base library load address to use.
      */
-    public void initServiceProcess(long baseLoadAddress) {
-        if (DEBUG) Log.i(TAG, "initServiceProcess(0x%x) called", baseLoadAddress);
-        synchronized (sLock) {
-            ensureInitializedLocked();
-            mInBrowserProcess = false;
-            mWaitForSharedRelros = true;
-            mBaseLoadAddress = baseLoadAddress;
-            mCurrentLoadAddress = baseLoadAddress;
-        }
-    }
+    public abstract void initServiceProcess(long baseLoadAddress);
 
     /**
      * Retrieve the base load address of all shared RELRO sections.
@@ -545,56 +436,6 @@
     abstract void loadLibraryImpl(String libFilePath, boolean isFixedAddressPermitted);
 
     /**
-     * Load the Linker JNI library. Throws UnsatisfiedLinkError on error.
-     */
-    @SuppressLint({"UnsafeDynamicallyLoadedCode"})
-    protected static void loadLinkerJniLibrary() {
-        LibraryLoader.setEnvForNative();
-        if (DEBUG) {
-            String libName = "lib" + LINKER_JNI_LIBRARY + ".so";
-            Log.i(TAG, "Loading %s", libName);
-        }
-        try {
-            System.loadLibrary(LINKER_JNI_LIBRARY);
-        } catch (UnsatisfiedLinkError e) {
-            if (LibraryLoader.PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) {
-                System.load(LibraryLoader.getExtractedLibraryPath(
-                        ContextUtils.getApplicationContext().getApplicationInfo(),
-                        LINKER_JNI_LIBRARY));
-            } else {
-                // Cannot continue if we cannot load the linker. Technically we could try to
-                // load the library with the system linker on Android M+, but this should never
-                // happen, better to catch it in crash reports.
-                throw e;
-            }
-        }
-    }
-
-    // Used internally to initialize the linker's data. Loads JNI.
-    @GuardedBy("sLock")
-    protected void ensureInitializedLocked() {
-        if (mInitialized) return;
-
-        loadLinkerJniLibrary();
-        mInitialized = true;
-    }
-
-    // Used internally to lazily setup the common random base load address.
-    @GuardedBy("sLock")
-    protected void setupBaseLoadAddressLocked() {
-        if (mBaseLoadAddress == -1) {
-            mBaseLoadAddress = getRandomBaseLoadAddress();
-            mCurrentLoadAddress = mBaseLoadAddress;
-            if (mBaseLoadAddress == 0) {
-                // If the random address is 0 there are issues with finding enough
-                // free address space, so disable RELRO shared / fixed load addresses.
-                Log.w(TAG, "Disabling shared RELROs due address space pressure");
-                mWaitForSharedRelros = false;
-            }
-        }
-    }
-
-    /**
      * Record information for a given library.
      * IMPORTANT: Native code knows about this class's fields, so
      * don't change them without modifying the corresponding C++ sources.
@@ -677,7 +518,7 @@
     }
 
     // Create a Bundle from a map of LibInfo objects.
-    protected static Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> map) {
+    protected Bundle createBundleFromLibInfoMap(HashMap<String, LibInfo> map) {
         Bundle bundle = new Bundle(map.size());
         for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
             bundle.putParcelable(entry.getKey(), entry.getValue());
@@ -686,7 +527,7 @@
     }
 
     // Create a new LibInfo map from a Bundle.
-    protected static HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bundle) {
+    protected HashMap<String, LibInfo> createLibInfoMapFromBundle(Bundle bundle) {
         HashMap<String, LibInfo> map = new HashMap<String, LibInfo>();
         for (String library : bundle.keySet()) {
             LibInfo libInfo = bundle.getParcelable(library);
@@ -696,7 +537,7 @@
     }
 
     // Call the close() method on all values of a LibInfo map.
-    protected static void closeLibInfoMap(HashMap<String, LibInfo> map) {
+    protected void closeLibInfoMap(HashMap<String, LibInfo> map) {
         for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
             entry.getValue().close();
         }
diff --git a/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java b/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java
deleted file mode 100644
index 8df1cf2..0000000
--- a/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.base.library_loader;
-
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.support.annotation.Nullable;
-
-import org.chromium.base.Log;
-import org.chromium.base.PathUtils;
-import org.chromium.base.annotations.JniIgnoreNatives;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-import javax.annotation.concurrent.GuardedBy;
-
-/**
- * Provides a concrete implementation of the Chromium Linker.
- *
- * This Linker implementation uses the Android M and later system linker to map Chrome and call
- * |JNI_OnLoad()|.
- *
- * For more on the operations performed by the Linker, see {@link Linker}.
- */
-@JniIgnoreNatives
-class ModernLinker extends Linker {
-    // Log tag for this class.
-    private static final String TAG = "ModernLinker";
-
-    // Becomes true once prepareLibraryLoad() has been called.
-    private boolean mPrepareLibraryLoadCalled;
-
-    // Library load path -> relro info.
-    private HashMap<String, LibInfo> mSharedRelros;
-
-    ModernLinker() {}
-
-    /**
-     * Call this method just before loading any native shared libraries in this process.
-     * Loads the Linker's JNI library, and initializes the variables involved in the
-     * implementation of shared RELROs.
-     */
-    @Override
-    void prepareLibraryLoad(@Nullable String apkFilePath) {
-        if (DEBUG) Log.i(TAG, "prepareLibraryLoad() called");
-
-        synchronized (sLock) {
-            assert !mPrepareLibraryLoadCalled;
-            ensureInitializedLocked();
-
-            // If in the browser, generate a random base load address for service processes
-            // and create an empty shared RELROs map. For service processes, the shared
-            // RELROs map must remain null until set by useSharedRelros().
-            if (mInBrowserProcess) {
-                setupBaseLoadAddressLocked();
-                mSharedRelros = new HashMap<String, LibInfo>();
-            }
-
-            // Create an empty loaded libraries map.
-            mLoadedLibraries = new HashMap<String, LibInfo>();
-
-            // Start the current load address at the base load address.
-            mCurrentLoadAddress = mBaseLoadAddress;
-
-            mPrepareLibraryLoadCalled = true;
-        }
-    }
-
-    /**
-     * Call this method just after loading all native shared libraries in this process.
-     * If not in the browser, closes the LibInfo entries used for RELRO sharing.
-     */
-    @Override
-    void finishLibraryLoad() {
-        if (DEBUG) Log.i(TAG, "finishLibraryLoad() called");
-
-        synchronized (sLock) {
-            assert mPrepareLibraryLoadCalled;
-
-            // Close shared RELRO file descriptors if not in the browser.
-            if (!mInBrowserProcess && mSharedRelros != null) {
-                closeLibInfoMap(mSharedRelros);
-                mSharedRelros = null;
-            }
-
-            // If testing, run tests now that all libraries are loaded and initialized.
-            if (NativeLibraries.sEnableLinkerTests) runTestRunnerClassForTesting(mInBrowserProcess);
-        }
-        if (DEBUG) Log.i(TAG, "finishLibraryLoad() done");
-    }
-
-    // Used internally to wait for shared RELROs. Returns once useSharedRelros() has been
-    // called to supply a valid shared RELROs bundle.
-    @GuardedBy("sLock")
-    private void waitForSharedRelrosLocked() {
-        if (DEBUG) Log.i(TAG, "waitForSharedRelros called");
-
-        // Return immediately if shared RELROs are already available.
-        if (mSharedRelros != null) return;
-
-        // Wait until notified by useSharedRelros() that shared RELROs have arrived.
-        long startTime = DEBUG ? SystemClock.uptimeMillis() : 0;
-        while (mSharedRelros == null) {
-            try {
-                sLock.wait();
-            } catch (InterruptedException e) {
-                // Continue waiting even if we were just interrupted.
-            }
-        }
-
-        if (DEBUG) {
-            Log.i(TAG, "Time to wait for shared RELRO: %d ms",
-                    SystemClock.uptimeMillis() - startTime);
-        }
-    }
-
-    /**
-     * Call this to send a Bundle containing the shared RELRO sections to be
-     * used in this process. If initServiceProcess() was previously called,
-     * libraryLoad() will wait until this method is called in another
-     * thread with a non-null value.
-     *
-     * @param bundle The Bundle instance containing a map of shared RELRO sections
-     * to use in this process.
-     */
-    @Override
-    public void useSharedRelros(Bundle bundle) {
-        if (DEBUG) Log.i(TAG, "useSharedRelros() called with " + bundle);
-
-        synchronized (sLock) {
-            mSharedRelros = createLibInfoMapFromBundle(bundle);
-            sLock.notifyAll();
-        }
-    }
-
-    /**
-     * Call this to retrieve the shared RELRO sections created in this process,
-     * after loading all libraries.
-     *
-     * @return a new Bundle instance, or null if RELRO sharing is disabled on
-     * this system, or if initServiceProcess() was called previously.
-     */
-    @Override
-    public Bundle getSharedRelros() {
-        if (DEBUG) Log.i(TAG, "getSharedRelros() called");
-        synchronized (sLock) {
-            if (!mInBrowserProcess) {
-                if (DEBUG) Log.i(TAG, "Not in browser, so returning null Bundle");
-                return null;
-            }
-
-            // Create a new Bundle containing RELRO section information for all the shared
-            // RELROs created while loading libraries.
-            if (mSharedRelrosBundle == null && mSharedRelros != null) {
-                mSharedRelrosBundle = createBundleFromLibInfoMap(mSharedRelros);
-                if (DEBUG) {
-                    Log.i(TAG, "Shared RELRO bundle created from map: %s", mSharedRelrosBundle);
-                }
-            }
-            if (DEBUG) Log.i(TAG, "Returning " + mSharedRelrosBundle);
-            return mSharedRelrosBundle;
-        }
-    }
-
-    /**
-     * Retrieve the base load address for libraries that share RELROs.
-     *
-     * @return a common, random base load address, or 0 if RELRO sharing is
-     * disabled.
-     */
-    @Override
-    public long getBaseLoadAddress() {
-        synchronized (sLock) {
-            ensureInitializedLocked();
-            setupBaseLoadAddressLocked();
-            if (DEBUG) Log.i(TAG, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress);
-
-            return mBaseLoadAddress;
-        }
-    }
-
-    /**
-     * Load a native shared library with the Chromium linker. If the zip file
-     * is not null, the shared library must be uncompressed and page aligned
-     * inside the zipfile. The library must not be the Chromium linker library.
-     *
-     * If asked to wait for shared RELROs, this function will block library loads
-     * until the shared RELROs bundle is received by useSharedRelros().
-     *
-     * @param libFilePath The path of the library (possibly in the zip file).
-     * @param isFixedAddressPermitted If true, uses a fixed load address if one was
-     * supplied, otherwise ignores the fixed address and loads wherever available.
-     */
-    @Override
-    void loadLibraryImpl(String libFilePath, boolean isFixedAddressPermitted) {
-        if (DEBUG) Log.i(TAG, "loadLibraryImpl: " + libFilePath + ", " + isFixedAddressPermitted);
-
-        synchronized (sLock) {
-            assert mPrepareLibraryLoadCalled;
-
-            String dlopenExtPath = libFilePath;
-            if (mLoadedLibraries.containsKey(dlopenExtPath)) {
-                if (DEBUG) Log.i(TAG, "Not loading %s twice", libFilePath);
-                return;
-            }
-
-            // The platform supports loading directly from the ZIP file, and as we are not sharing
-            // relocations anyway, let the system linker load the library.
-            if (!isFixedAddressPermitted) {
-                System.loadLibrary(libFilePath);
-                return;
-            }
-
-            // If not in the browser and shared RELROs are not disabled, load the library at a fixed
-            // address. Otherwise, load anywhere.
-            long loadAddress = 0;
-            if (mWaitForSharedRelros) {
-                loadAddress = mCurrentLoadAddress;
-
-                // For multiple libraries, ensure we stay within reservation range.
-                if (loadAddress > mBaseLoadAddress + ADDRESS_SPACE_RESERVATION) {
-                    String errorMessage = "Load address outside reservation, for: " + libFilePath;
-                    Log.e(TAG, errorMessage);
-                    throw new UnsatisfiedLinkError(errorMessage);
-                }
-            }
-
-            LibInfo libInfo = new LibInfo();
-            boolean alreadyLoaded = false;
-            if (mInBrowserProcess && mCurrentLoadAddress != 0) {
-                // We are in the browser, and with a current load address that indicates that
-                // there is enough address space for shared RELRO to operate. Create the
-                // shared RELRO, and store it in the map.
-                String relroPath = PathUtils.getDataDirectory() + "/RELRO:" + libFilePath;
-                if (nativeLoadLibraryCreateRelros(
-                            dlopenExtPath, mCurrentLoadAddress, relroPath, libInfo)) {
-                    mSharedRelros.put(dlopenExtPath, libInfo);
-                    alreadyLoaded = true;
-                } else {
-                    String errorMessage = "Unable to create shared relro: " + relroPath;
-                    Log.w(TAG, errorMessage);
-                }
-            } else if (!mInBrowserProcess && mCurrentLoadAddress != 0 && mWaitForSharedRelros) {
-                // We are in a service process, again with a current load address that is
-                // suitable for shared RELRO, and we are to wait for shared RELROs. So
-                // do that, then use the map we receive to provide libinfo for library load.
-                waitForSharedRelrosLocked();
-                if (mSharedRelros.containsKey(dlopenExtPath)) {
-                    libInfo = mSharedRelros.get(dlopenExtPath);
-                }
-            }
-
-            // Load the library. In the browser, loadAddress is 0, so nativeLoadLibrary()
-            // will load without shared RELRO. Otherwise, it uses shared RELRO if the attached
-            // libInfo is usable.
-            if (!alreadyLoaded) {
-                if (!nativeLoadLibraryUseRelros(dlopenExtPath, loadAddress, libInfo.mRelroFd)) {
-                    String errorMessage = "Unable to load library: " + dlopenExtPath;
-                    Log.e(TAG, errorMessage);
-                    throw new UnsatisfiedLinkError(errorMessage);
-                }
-            }
-            // Print the load address to the logcat when testing the linker. The format
-            // of the string is expected by the Python test_runner script as one of:
-            //    BROWSER_LIBRARY_ADDRESS: <library-name> <address>
-            //    RENDERER_LIBRARY_ADDRESS: <library-name> <address>
-            // Where <library-name> is the library name, and <address> is the hexadecimal load
-            // address.
-            if (NativeLibraries.sEnableLinkerTests) {
-                String tag =
-                        mInBrowserProcess ? "BROWSER_LIBRARY_ADDRESS" : "RENDERER_LIBRARY_ADDRESS";
-                Log.i(TAG,
-                        String.format(
-                                Locale.US, "%s: %s %x", tag, libFilePath, libInfo.mLoadAddress));
-            }
-
-            if (loadAddress != 0 && mCurrentLoadAddress != 0) {
-                // Compute the next current load address. If mCurrentLoadAddress
-                // is not 0, this is an explicit library load address.
-                mCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize;
-            }
-
-            mLoadedLibraries.put(dlopenExtPath, libInfo);
-            if (DEBUG) Log.i(TAG, "Library details %s", libInfo.toString());
-        }
-    }
-
-    /**
-     * Native method to return the CPU ABI.
-     * Obtaining it from the linker's native code means that we always correctly
-     * match the loaded library's ABI to the linker's ABI.
-     *
-     * @return CPU ABI string.
-     */
-    private static native String nativeGetCpuAbi();
-
-    private static native boolean nativeLoadLibraryCreateRelros(
-            String dlopenExtPath, long loadAddress, String relroPath, LibInfo libInfo);
-    private static native boolean nativeLoadLibraryUseRelros(
-            String dlopenExtPath, long loadAddress, int fd);
-}
diff --git a/base/android/linker/BUILD.gn b/base/android/linker/BUILD.gn
index 5e15cd5..1f1821d 100644
--- a/base/android/linker/BUILD.gn
+++ b/base/android/linker/BUILD.gn
@@ -13,8 +13,6 @@
     "legacy_linker_jni.h",
     "linker_jni.cc",
     "linker_jni.h",
-    "modern_linker_jni.cc",
-    "modern_linker_jni.h",
   ]
 
   # The NDK contains the crazy_linker here:
diff --git a/base/android/linker/legacy_linker_jni.cc b/base/android/linker/legacy_linker_jni.cc
index a98ff01..0a2477e0 100644
--- a/base/android/linker/legacy_linker_jni.cc
+++ b/base/android/linker/legacy_linker_jni.cc
@@ -67,6 +67,25 @@
   crazy_library_t* lib_;
 };
 
+// We identify the abi tag for which the linker is running. This allows
+// us to select the library which matches the abi of the linker.
+
+#if defined(__arm__) && defined(__ARM_ARCH_7A__)
+#define CURRENT_ABI "armeabi-v7a"
+#elif defined(__arm__)
+#define CURRENT_ABI "armeabi"
+#elif defined(__i386__)
+#define CURRENT_ABI "x86"
+#elif defined(__mips__)
+#define CURRENT_ABI "mips"
+#elif defined(__x86_64__)
+#define CURRENT_ABI "x86_64"
+#elif defined(__aarch64__)
+#define CURRENT_ABI "arm64-v8a"
+#else
+#error "Unsupported target abi"
+#endif
+
 // Add a zip archive file path to the context's current search path
 // list. Making it possible to load libraries directly from it.
 JNI_GENERATOR_EXPORT bool
@@ -107,7 +126,7 @@
     jobject lib_info_obj) {
   String library_name(env, lib_name_obj);
   LOG_INFO("Called for %s, at address 0x%llx", library_name.c_str(),
-           static_cast<unsigned long long>(load_address));
+           load_address);
   crazy_context_t* context = GetCrazyContext();
 
   if (!IsValidAddress(load_address)) {
diff --git a/base/android/linker/linker_jni.cc b/base/android/linker/linker_jni.cc
index 39711e0..dbd3508 100644
--- a/base/android/linker/linker_jni.cc
+++ b/base/android/linker/linker_jni.cc
@@ -20,7 +20,6 @@
 #include <sys/mman.h>
 
 #include "legacy_linker_jni.h"
-#include "modern_linker_jni.h"
 
 namespace chromium_android_linker {
 
@@ -133,8 +132,7 @@
   }
 
   // Initialize linker base and implementations.
-  if (!LinkerJNIInit(vm, env) || !LegacyLinkerJNIInit(vm, env) ||
-      !ModernLinkerJNIInit(vm, env)) {
+  if (!LinkerJNIInit(vm, env) || !LegacyLinkerJNIInit(vm, env)) {
     return -1;
   }
 
diff --git a/base/android/linker/linker_jni.h b/base/android/linker/linker_jni.h
index 7c3abec..fe761a33 100644
--- a/base/android/linker/linker_jni.h
+++ b/base/android/linker/linker_jni.h
@@ -43,6 +43,13 @@
 
 #define UNUSED __attribute__((unused))
 
+// See commentary in crazy_linker_elf_loader.cpp for the effect of setting
+// this. If changing there, change here also.
+//
+// For more, see:
+//   https://crbug.com/504410
+#define RESERVE_BREAKPAD_GUARD_REGION 1
+
 #if defined(ARCH_CPU_X86)
 // Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
 // x86 - use force_align_arg_pointer to realign the stack at the JNI
@@ -53,27 +60,18 @@
 #define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
 #endif
 
-#if defined(__arm__) && defined(__ARM_ARCH_7A__)
-#define CURRENT_ABI "armeabi-v7a"
-#elif defined(__arm__)
-#define CURRENT_ABI "armeabi"
-#elif defined(__i386__)
-#define CURRENT_ABI "x86"
-#elif defined(__mips__)
-#define CURRENT_ABI "mips"
-#elif defined(__x86_64__)
-#define CURRENT_ABI "x86_64"
-#elif defined(__aarch64__)
-#define CURRENT_ABI "arm64-v8a"
-#else
-#error "Unsupported target abi"
-#endif
-
 namespace chromium_android_linker {
 
 // Larger than the largest library we might attempt to load.
 static const size_t kAddressSpaceReservationSize = 192 * 1024 * 1024;
 
+// Size of any Breakpad guard region. 16MB is comfortably larger than the
+// ~6MB relocation packing of the current 64-bit libchrome.so, the largest we
+// expect to encounter.
+#if RESERVE_BREAKPAD_GUARD_REGION
+static const size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024;
+#endif
+
 // A simple scoped UTF String class that can be initialized from
 // a Java jstring handle. Modeled like std::string, which cannot
 // be used here.
diff --git a/base/android/linker/modern_linker_jni.cc b/base/android/linker/modern_linker_jni.cc
deleted file mode 100644
index aa842160..0000000
--- a/base/android/linker/modern_linker_jni.cc
+++ /dev/null
@@ -1,517 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Uses android_dlopen_ext() to share relocations.
-
-// This source code *cannot* depend on anything from base/ or the C++
-// STL, to keep the final library small, and avoid ugly dependency issues.
-
-#include "modern_linker_jni.h"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <jni.h>
-#include <limits.h>
-#include <link.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android/dlext.h>
-#include "linker_jni.h"
-
-// Not defined on all platforms. As this linker is only supported on ARM32/64,
-// x86/x86_64 and MIPS, page size is always 4k.
-#if !defined(PAGE_SIZE)
-#define PAGE_SIZE (1 << 12)
-#define PAGE_MASK (~(PAGE_SIZE - 1))
-#endif
-
-#define PAGE_START(x) ((x)&PAGE_MASK)
-#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
-
-namespace chromium_android_linker {
-namespace {
-
-// Record of the Java VM passed to JNI_OnLoad().
-static JavaVM* s_java_vm = nullptr;
-
-// Convenience wrapper around dlsym() on the main executable. Returns
-// the address of the requested symbol, or nullptr if not found. Status
-// is available from dlerror().
-void* Dlsym(const char* symbol_name) {
-  static void* handle = nullptr;
-
-  if (!handle)
-    handle = dlopen(nullptr, RTLD_NOW);
-
-  void* result = dlsym(handle, symbol_name);
-  return result;
-}
-
-// dl_iterate_phdr() wrapper, accessed via dlsym lookup. Done this way.
-// so that this code compiles for Android versions that are too early to
-// offer it. Checks in LibraryLoader.java should ensure that we
-// never reach here at runtime on Android versions that are too old to
-// supply dl_iterate_phdr; that is, earlier than Android M. Returns
-// false if no dl_iterate_phdr() is available, otherwise true with the
-// return value from dl_iterate_phdr() in |status|.
-bool DlIteratePhdr(int (*callback)(dl_phdr_info*, size_t, void*),
-                   void* data,
-                   int* status) {
-  using DlIteratePhdrCallback = int (*)(dl_phdr_info*, size_t, void*);
-  using DlIteratePhdrFunctionPtr = int (*)(DlIteratePhdrCallback, void*);
-  static DlIteratePhdrFunctionPtr function_ptr = nullptr;
-
-  if (!function_ptr) {
-    function_ptr =
-        reinterpret_cast<DlIteratePhdrFunctionPtr>(Dlsym("dl_iterate_phdr"));
-    if (!function_ptr) {
-      LOG_ERROR("dlsym: dl_iterate_phdr: %s", dlerror());
-      return false;
-    }
-  }
-
-  *status = (*function_ptr)(callback, data);
-  return true;
-}
-
-// Convenience struct wrapper around android_dlextinfo.
-struct AndroidDlextinfo {
-  AndroidDlextinfo(int flags,
-                   void* reserved_addr,
-                   size_t reserved_size,
-                   int relro_fd) {
-    memset(&extinfo, 0, sizeof(extinfo));
-    extinfo.flags = flags;
-    extinfo.reserved_addr = reserved_addr;
-    extinfo.reserved_size = reserved_size;
-    extinfo.relro_fd = relro_fd;
-  }
-
-  android_dlextinfo extinfo;
-};
-
-// android_dlopen_ext() wrapper, accessed via dlsym lookup. Returns false
-// if no android_dlopen_ext() is available, otherwise true with the return
-// value from android_dlopen_ext() in |status|.
-bool AndroidDlopenExt(const char* filename,
-                      int flag,
-                      const AndroidDlextinfo* dlextinfo,
-                      void** status) {
-  using DlopenExtFunctionPtr =
-      void* (*)(const char*, int, const android_dlextinfo*);
-  static DlopenExtFunctionPtr function_ptr = nullptr;
-
-  if (!function_ptr) {
-    function_ptr =
-        reinterpret_cast<DlopenExtFunctionPtr>(Dlsym("android_dlopen_ext"));
-    if (!function_ptr) {
-      LOG_ERROR("dlsym: android_dlopen_ext: %s", dlerror());
-      return false;
-    }
-  }
-
-  android_dlextinfo ext = dlextinfo->extinfo;
-  LOG_INFO(
-      "android_dlopen_ext:"
-      " flags=0x%llx, reserved_addr=%p, reserved_size=%d, relro_fd=%d",
-      static_cast<long long>(ext.flags), ext.reserved_addr,
-      static_cast<int>(ext.reserved_size), ext.relro_fd);
-
-  *status = (*function_ptr)(filename, flag, &ext);
-  return true;
-}
-
-// Callback data for FindLoadedLibrarySize().
-struct CallbackData {
-  explicit CallbackData(void* address)
-      : load_address(address), load_size(0), min_vaddr(0) {}
-
-  const void* load_address;
-  size_t load_size;
-  size_t min_vaddr;
-};
-
-// Callback for dl_iterate_phdr(). Read phdrs to identify whether or not
-// this library's load address matches the |load_address| passed in
-// |data|. If yes, pass back load size and min vaddr via |data|. A non-zero
-// return value terminates iteration.
-int FindLoadedLibrarySize(dl_phdr_info* info, size_t size UNUSED, void* data) {
-  CallbackData* callback_data = reinterpret_cast<CallbackData*>(data);
-
-  // Use max and min vaddr to compute the library's load size.
-  ElfW(Addr) min_vaddr = ~0;
-  ElfW(Addr) max_vaddr = 0;
-
-  bool is_matching = false;
-  for (size_t i = 0; i < info->dlpi_phnum; ++i) {
-    const ElfW(Phdr)* phdr = &info->dlpi_phdr[i];
-    if (phdr->p_type != PT_LOAD)
-      continue;
-
-    // See if this segment's load address matches what we passed to
-    // android_dlopen_ext as extinfo.reserved_addr.
-    void* load_addr = reinterpret_cast<void*>(info->dlpi_addr + phdr->p_vaddr);
-    if (load_addr == callback_data->load_address)
-      is_matching = true;
-
-    if (phdr->p_vaddr < min_vaddr)
-      min_vaddr = phdr->p_vaddr;
-    if (phdr->p_vaddr + phdr->p_memsz > max_vaddr)
-      max_vaddr = phdr->p_vaddr + phdr->p_memsz;
-  }
-
-  // If this library matches what we seek, return its load size.
-  if (is_matching) {
-    int page_size = sysconf(_SC_PAGESIZE);
-    if (page_size != PAGE_SIZE)
-      abort();
-
-    callback_data->load_size = PAGE_END(max_vaddr) - PAGE_START(min_vaddr);
-    callback_data->min_vaddr = min_vaddr;
-    return true;
-  }
-
-  return false;
-}
-
-// Helper class for anonymous memory mapping.
-class ScopedAnonymousMmap {
- public:
-  static ScopedAnonymousMmap ReserveAtAddress(void* address, size_t size);
-
-  ~ScopedAnonymousMmap() {
-    if (addr_ && owned_)
-      munmap(addr_, size_);
-  }
-
-  ScopedAnonymousMmap(ScopedAnonymousMmap&& o) {
-    addr_ = o.addr_;
-    size_ = o.size_;
-    owned_ = o.owned_;
-    o.Release();
-  }
-
-  void* address() const { return addr_; }
-  size_t size() const { return size_; }
-  void Release() { owned_ = false; }
-
- private:
-  ScopedAnonymousMmap() = default;
-  ScopedAnonymousMmap(void* addr, size_t size) : addr_(addr), size_(size) {}
-
- private:
-  bool owned_ = true;
-  void* addr_ = nullptr;
-  size_t size_ = 0;
-
-  // Move only.
-  ScopedAnonymousMmap(const ScopedAnonymousMmap&) = delete;
-  ScopedAnonymousMmap& operator=(const ScopedAnonymousMmap&) = delete;
-};
-
-// Makes sure the file descriptor is closed unless |Release()| is called.
-class ScopedFileDescriptor {
- public:
-  ScopedFileDescriptor(int fd) : fd_(fd), owned_(true) {}
-  ~ScopedFileDescriptor() {
-    if (owned_)
-      Close();
-  }
-  ScopedFileDescriptor(ScopedFileDescriptor&& o)
-      : fd_(o.fd_), owned_(o.owned_) {
-    o.owned_ = false;
-  }
-  int get() const { return fd_; }
-  void Release() { owned_ = false; }
-  void Close() {
-    if (fd_ != -1)
-      close(fd_);
-    owned_ = false;
-  }
-
- private:
-  const int fd_;
-  bool owned_;
-
-  // Move only.
-  ScopedFileDescriptor(const ScopedFileDescriptor&) = delete;
-  ScopedFileDescriptor& operator=(const ScopedFileDescriptor&) = delete;
-};
-
-// Reserves an address space range, starting at |address|.
-// If successful, returns a valid mapping, otherwise returns an empty one.
-ScopedAnonymousMmap ScopedAnonymousMmap::ReserveAtAddress(void* address,
-                                                          size_t size) {
-  void* actual_address =
-      mmap(address, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (actual_address == MAP_FAILED) {
-    LOG_INFO("mmap failed: %s", strerror(errno));
-    return {};
-  }
-
-  if (actual_address && actual_address != address) {
-    LOG_ERROR("Failed to obtain fixed address for load");
-    return {};
-  }
-
-  return {actual_address, size};
-}
-
-// Returns the actual size of the library loaded at |addr| in |load_size|, and
-// the min vaddr in |min_vaddr|. Returns false if the library appears not to be
-// loaded.
-bool GetLibraryLoadSize(void* addr, size_t* load_size, size_t* min_vaddr) {
-  LOG_INFO("Called for %p", addr);
-
-  // Find the real load size and min vaddr for the library loaded at |addr|.
-  CallbackData callback_data(addr);
-  int status = 0;
-  if (!DlIteratePhdr(&FindLoadedLibrarySize, &callback_data, &status)) {
-    LOG_ERROR("No dl_iterate_phdr function found");
-    return false;
-  }
-  if (!status) {
-    LOG_ERROR("Failed to find library at address %p", addr);
-    return false;
-  }
-
-  *load_size = callback_data.load_size;
-  *min_vaddr = callback_data.min_vaddr;
-  return true;
-}
-
-// Reopens |fd| that was initially opened from |path| as a read-only fd.
-// Deletes the file in the process, and returns the new read only file
-// descriptor in case of success, -1 otherwise.
-ScopedFileDescriptor ReopenReadOnly(const String& path,
-                                    ScopedFileDescriptor original_fd) {
-  const char* filepath = path.c_str();
-  original_fd.Close();
-  ScopedFileDescriptor scoped_fd{open(filepath, O_RDONLY)};
-  if (scoped_fd.get() == -1) {
-    LOG_ERROR("open: %s: %s", path.c_str(), strerror(errno));
-    return -1;
-  }
-
-  // Delete the directory entry for the RELRO file. The fd we hold ensures
-  // that its data remains intact.
-  if (unlink(filepath) == -1) {
-    LOG_ERROR("unlink: %s: %s", filepath, strerror(errno));
-    return -1;
-  }
-  return scoped_fd;
-}
-
-// Resizes the address space reservation to the actual required size.
-// Failure here is only a warning, as at worst this wastes virtual address
-// space, not actual memory.
-void ResizeMapping(const ScopedAnonymousMmap& mapping) {
-  // After loading we can find the actual size of the library. It should
-  // be less than the space we reserved for it.
-  size_t load_size = 0;
-  size_t min_vaddr = 0;
-  if (!GetLibraryLoadSize(mapping.address(), &load_size, &min_vaddr)) {
-    LOG_ERROR("Unable to find size for load at %p", mapping.address());
-    return;
-  }
-
-  // Trim the reservation mapping to match the library's actual size. Failure
-  // to resize is not a fatal error. At worst we lose a portion of virtual
-  // address space that we might otherwise have recovered. Note that trimming
-  // the mapping here requires that we have already released the scoped
-  // mapping.
-  const uintptr_t uintptr_addr = reinterpret_cast<uintptr_t>(mapping.address());
-  if (mapping.size() > load_size) {
-    // Unmap the part of the reserved address space that is beyond the end of
-    // the loaded library data.
-    void* unmap = reinterpret_cast<void*>(uintptr_addr + load_size);
-    const size_t length = mapping.size() - load_size;
-    if (munmap(unmap, length) == -1) {
-      LOG_ERROR("WARNING: unmap of %d bytes at %p failed: %s",
-                static_cast<int>(length), unmap, strerror(errno));
-    }
-  } else {
-    LOG_ERROR("WARNING: library reservation was too small");
-  }
-}
-
-// Calls JNI_OnLoad() in the library referenced by |handle|.
-// Returns true for success.
-bool CallJniOnLoad(void* handle) {
-  // Locate and if found then call the loaded library's JNI_OnLoad() function.
-  using JNI_OnLoadFunctionPtr = int (*)(void* vm, void* reserved);
-  auto jni_onload =
-      reinterpret_cast<JNI_OnLoadFunctionPtr>(dlsym(handle, "JNI_OnLoad"));
-  if (jni_onload != nullptr) {
-    // Check that JNI_OnLoad returns a usable JNI version.
-    int jni_version = (*jni_onload)(s_java_vm, nullptr);
-    if (jni_version < JNI_VERSION_1_4) {
-      LOG_ERROR("JNI version is invalid: %d", jni_version);
-      return false;
-    }
-  }
-  return true;
-}
-
-// Load the library at |path| at address |wanted_address| if possible, and
-// creates a file with relro at |relocations_path|.
-//
-// In case of success, returns a readonly file descriptor to the relocations,
-// otherwise returns -1.
-int LoadCreateSharedRelocations(const String& path,
-                                void* wanted_address,
-                                const String& relocations_path) {
-  LOG_INFO("Entering");
-  ScopedAnonymousMmap mapping = ScopedAnonymousMmap::ReserveAtAddress(
-      wanted_address, kAddressSpaceReservationSize);
-  if (!mapping.address())
-    return -1;
-
-  unlink(relocations_path.c_str());
-  ScopedFileDescriptor relro_fd = ScopedFileDescriptor{open(
-      relocations_path.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)};
-  if (relro_fd.get() == -1) {
-    LOG_ERROR("open: %s: %s", relocations_path.c_str(), strerror(errno));
-    return -1;
-  }
-
-  int flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO;
-  AndroidDlextinfo dlextinfo(flags, mapping.address(), mapping.size(),
-                             relro_fd.get());
-  void* handle = nullptr;
-  if (!AndroidDlopenExt(path.c_str(), RTLD_NOW, &dlextinfo, &handle)) {
-    LOG_ERROR("No android_dlopen_ext function found");
-    return -1;
-  }
-  if (handle == nullptr) {
-    LOG_ERROR("android_dlopen_ext: %s", dlerror());
-    return -1;
-  }
-
-  mapping.Release();
-  ResizeMapping(mapping);
-  if (!CallJniOnLoad(handle)) {
-    unlink(relocations_path.c_str());
-    return false;
-  }
-  ScopedFileDescriptor scoped_fd =
-      ReopenReadOnly(relocations_path, std::move(relro_fd));
-  scoped_fd.Release();
-  return scoped_fd.get();
-}
-
-// Load the library at |path| at address |wanted_address| if possible, and
-// uses the relocations in |relocations_fd| if possible.
-bool LoadUseSharedRelocations(const String& path,
-                              void* wanted_address,
-                              int relocations_fd) {
-  LOG_INFO("Entering");
-  ScopedAnonymousMmap mapping = ScopedAnonymousMmap::ReserveAtAddress(
-      wanted_address, kAddressSpaceReservationSize);
-  if (!mapping.address())
-    return false;
-
-  int flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO;
-  AndroidDlextinfo dlextinfo(flags, mapping.address(), mapping.size(),
-                             relocations_fd);
-  void* handle = nullptr;
-  if (!AndroidDlopenExt(path.c_str(), RTLD_NOW, &dlextinfo, &handle)) {
-    LOG_ERROR("No android_dlopen_ext function found");
-    return false;
-  }
-  if (handle == nullptr) {
-    LOG_ERROR("android_dlopen_ext: %s", dlerror());
-    return false;
-  }
-
-  mapping.Release();
-  ResizeMapping(mapping);
-  if (!CallJniOnLoad(handle))
-    return false;
-
-  return true;
-}
-
-}  // namespace
-
-// Get the CPU ABI string for which the linker is running.
-//
-// The returned string is used to construct the path to libchrome.so when
-// loading directly from APK.
-//
-// |env| is the current JNI environment handle.
-// |clazz| is the static class handle for org.chromium.base.Linker,
-// and is ignored here.
-// Returns the CPU ABI string for which the linker is running.
-JNI_GENERATOR_EXPORT jstring
-Java_org_chromium_base_library_1loader_ModernLinker_nativeGetCpuAbi(
-    JNIEnv* env,
-    jclass clazz) {
-  return env->NewStringUTF(CURRENT_ABI);
-}
-
-JNI_GENERATOR_EXPORT jboolean
-Java_org_chromium_base_library_1loader_ModernLinker_nativeLoadLibraryCreateRelros(
-    JNIEnv* env,
-    jclass clazz,
-    jstring jdlopen_ext_path,
-    jlong load_address,
-    jstring jrelro_path,
-    jobject lib_info_obj) {
-  LOG_INFO("Entering");
-
-  String library_path(env, jdlopen_ext_path);
-  String relro_path(env, jrelro_path);
-
-  if (!IsValidAddress(load_address)) {
-    LOG_ERROR("Invalid address 0x%llx", static_cast<long long>(load_address));
-    return false;
-  }
-  void* address = reinterpret_cast<void*>(load_address);
-
-  int fd = LoadCreateSharedRelocations(library_path, address, relro_path);
-  if (fd == -1)
-    return false;
-
-  // Note the shared RELRO fd in the supplied libinfo object. In this
-  // implementation the RELRO start is set to the library's load address,
-  // and the RELRO size is unused.
-  const size_t cast_addr = reinterpret_cast<size_t>(address);
-  s_lib_info_fields.SetRelroInfo(env, lib_info_obj, cast_addr, 0, fd);
-
-  return true;
-}
-
-JNI_GENERATOR_EXPORT jboolean
-Java_org_chromium_base_library_1loader_ModernLinker_nativeLoadLibraryUseRelros(
-    JNIEnv* env,
-    jclass clazz,
-    jstring jdlopen_ext_path,
-    jlong load_address,
-    jint relro_fd) {
-  LOG_INFO("Entering");
-
-  String library_path(env, jdlopen_ext_path);
-
-  if (!IsValidAddress(load_address)) {
-    LOG_ERROR("Invalid address 0x%llx", static_cast<long long>(load_address));
-    return false;
-  }
-  void* address = reinterpret_cast<void*>(load_address);
-
-  return LoadUseSharedRelocations(library_path, address, relro_fd);
-}
-
-bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env) {
-  s_java_vm = vm;
-  return true;
-}
-
-}  // namespace chromium_android_linker
diff --git a/base/android/linker/modern_linker_jni.h b/base/android/linker/modern_linker_jni.h
deleted file mode 100644
index c4b92a5..0000000
--- a/base/android/linker/modern_linker_jni.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
-#define BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
-
-#include <jni.h>
-
-namespace chromium_android_linker {
-
-// JNI_OnLoad() initialization hook for the modern linker.
-// Sets up JNI and other initializations for native linker code.
-// |vm| is the Java VM handle passed to JNI_OnLoad().
-// |env| is the current JNI environment handle.
-// On success, returns true.
-extern bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env);
-
-}  // namespace chromium_android_linker
-
-#endif  // BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
diff --git a/base/fuchsia/default_context.cc b/base/fuchsia/default_context.cc
new file mode 100644
index 0000000..96d6a0c
--- /dev/null
+++ b/base/fuchsia/default_context.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/fuchsia/default_context.h"
+
+#include <lib/sys/cpp/component_context.h>
+
+#include "base/fuchsia/file_utils.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+
+namespace base {
+namespace fuchsia {
+
+// Returns default sys::ComponentContext for the current process.
+sys::ComponentContext* ComponentContextForCurrentProcess() {
+  static base::NoDestructor<std::unique_ptr<sys::ComponentContext>> value(
+      sys::ComponentContext::Create());
+  return value.get()->get();
+}
+
+}  // namespace fuchsia
+}  // namespace base
diff --git a/base/fuchsia/default_context.h b/base/fuchsia/default_context.h
new file mode 100644
index 0000000..042e733
--- /dev/null
+++ b/base/fuchsia/default_context.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FUCHSIA_DEFAULT_CONTEXT_H_
+#define BASE_FUCHSIA_DEFAULT_CONTEXT_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+
+namespace sys {
+class ComponentContext;
+}  // namespace sys
+
+namespace base {
+namespace fuchsia {
+
+// Returns default sys::ComponentContext for the current process.
+BASE_EXPORT sys::ComponentContext* ComponentContextForCurrentProcess();
+
+}  // namespace fuchsia
+}  // namespace base
+
+#endif  // BASE_FUCHSIA_DEFAULT_CONTEXT_H_
\ No newline at end of file
diff --git a/base/fuchsia/filtered_service_directory.cc b/base/fuchsia/filtered_service_directory.cc
index 02cf09a5..77c07c3 100644
--- a/base/fuchsia/filtered_service_directory.cc
+++ b/base/fuchsia/filtered_service_directory.cc
@@ -4,51 +4,42 @@
 
 #include "base/fuchsia/filtered_service_directory.h"
 
-#include <lib/fdio/directory.h>
+#include <lib/async/default.h>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/service_directory_client.h"
 
 namespace base {
 namespace fuchsia {
 
 FilteredServiceDirectory::FilteredServiceDirectory(
-    const ServiceDirectoryClient* directory)
-    : directory_(directory) {
-  outgoing_directory_ = std::make_unique<ServiceDirectory>(
-      outgoing_directory_client_.NewRequest());
+    sys::ServiceDirectory* directory)
+    : directory_(std::move(directory)) {
+  outgoing_directory_.Serve(
+      outgoing_directory_client_.NewRequest().TakeChannel());
 }
 
-FilteredServiceDirectory::~FilteredServiceDirectory() {
-  outgoing_directory_->RemoveAllServices();
-}
+FilteredServiceDirectory::~FilteredServiceDirectory() {}
 
 void FilteredServiceDirectory::AddService(const char* service_name) {
-  outgoing_directory_->AddServiceUnsafe(
-      service_name,
-      base::BindRepeating(&FilteredServiceDirectory::HandleRequest,
-                          base::Unretained(this), service_name));
+  outgoing_directory_.AddPublicService(
+      std::make_unique<vfs::Service>(
+          [this, service_name](zx::channel channel,
+                               async_dispatcher_t* dispatcher) {
+            DCHECK_EQ(dispatcher, async_get_default_dispatcher());
+            directory_->Connect(service_name, std::move(channel));
+          }),
+      service_name);
 }
 
-fidl::InterfaceHandle<::fuchsia::io::Directory>
-FilteredServiceDirectory::ConnectClient() {
-  fidl::InterfaceHandle<::fuchsia::io::Directory> client;
-
-  // ServiceDirectory puts public services under ./svc . Connect to that
+void FilteredServiceDirectory::ConnectClient(
+    fidl::InterfaceRequest<::fuchsia::io::Directory> dir_request) {
+  // sys::OutgoingDirectory puts public services under ./svc . Connect to that
   // directory and return client handle for the connection,
-  zx_status_t status =
-      fdio_service_connect_at(outgoing_directory_client_.channel().get(), "svc",
-                              client.NewRequest().TakeChannel().release());
-  ZX_CHECK(status == ZX_OK, status) << "fdio_service_connect_at()";
-
-  return client;
-}
-
-void FilteredServiceDirectory::HandleRequest(const char* service_name,
-                                             zx::channel channel) {
-  directory_->ConnectToServiceUnsafe(service_name, std::move(channel));
+  outgoing_directory_.GetOrCreateDirectory("svc")->Serve(
+      ::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
+      dir_request.TakeChannel());
 }
 
 }  // namespace fuchsia
diff --git a/base/fuchsia/filtered_service_directory.h b/base/fuchsia/filtered_service_directory.h
index e484ec1..8b4bce3 100644
--- a/base/fuchsia/filtered_service_directory.h
+++ b/base/fuchsia/filtered_service_directory.h
@@ -5,41 +5,39 @@
 #ifndef BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_
 #define BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_
 
-#include "base/fuchsia/service_directory.h"
-
 #include <fuchsia/io/cpp/fidl.h>
 #include <lib/fidl/cpp/interface_handle.h>
+#include <lib/sys/cpp/outgoing_directory.h>
+#include <lib/sys/cpp/service_directory.h>
 #include <lib/zx/channel.h>
 #include <memory>
 
+#include "base/base_export.h"
 #include "base/macros.h"
 
 namespace base {
 namespace fuchsia {
 
-class ServiceDirectoryClient;
-
 // ServiceDirectory that uses the supplied ServiceDirectoryClient to satisfy
 // requests for only a restricted set of services.
 class BASE_EXPORT FilteredServiceDirectory {
  public:
-  // Creates proxy that proxies requests to the specified service |directory|,
-  // which must outlive the proxy.
-  explicit FilteredServiceDirectory(const ServiceDirectoryClient* directory);
+  // Creates a directory that proxies requests to the specified service
+  // |directory|.
+  explicit FilteredServiceDirectory(sys::ServiceDirectory* directory);
   ~FilteredServiceDirectory();
 
   // Adds the specified service to the list of whitelisted services.
   void AddService(const char* service_name);
 
-  // Returns a client channel connected to the directory. The returned channel
-  // can be passed to a sandboxed process to be used for /svc namespace.
-  fidl::InterfaceHandle<::fuchsia::io::Directory> ConnectClient();
+  // Connects a directory client. The directory can be passed to a sandboxed
+  // process to be used for /svc namespace.
+  void ConnectClient(
+      fidl::InterfaceRequest<::fuchsia::io::Directory> dir_request);
 
  private:
-  void HandleRequest(const char* service_name, zx::channel channel);
-
-  const ServiceDirectoryClient* const directory_;
-  std::unique_ptr<ServiceDirectory> outgoing_directory_;
+  const sys::ServiceDirectory* const directory_;
+  sys::OutgoingDirectory outgoing_directory_;
 
   // Client side of the channel used by |outgoing_directory_|.
   fidl::InterfaceHandle<::fuchsia::io::Directory> outgoing_directory_client_;
diff --git a/base/fuchsia/filtered_service_directory_unittest.cc b/base/fuchsia/filtered_service_directory_unittest.cc
index 6d5b27c..e58ef11 100644
--- a/base/fuchsia/filtered_service_directory_unittest.cc
+++ b/base/fuchsia/filtered_service_directory_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/fuchsia/service_directory_client.h"
 #include "base/fuchsia/service_directory_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,21 +16,23 @@
  public:
   FilteredServiceDirectoryTest() {
     filtered_service_directory_ = std::make_unique<FilteredServiceDirectory>(
-        public_service_directory_client_.get());
-    filtered_client_ = std::make_unique<ServiceDirectoryClient>(
-        filtered_service_directory_->ConnectClient());
+        public_service_directory_.get());
+    fidl::InterfaceHandle<::fuchsia::io::Directory> directory;
+    filtered_service_directory_->ConnectClient(directory.NewRequest());
+    filtered_client_ =
+        std::make_unique<sys::ServiceDirectory>(std::move(directory));
   }
 
  protected:
   std::unique_ptr<FilteredServiceDirectory> filtered_service_directory_;
-  std::unique_ptr<ServiceDirectoryClient> filtered_client_;
+  std::unique_ptr<sys::ServiceDirectory> filtered_client_;
 };
 
 // Verify that we can connect to a whitelisted service.
 TEST_F(FilteredServiceDirectoryTest, Connect) {
   filtered_service_directory_->AddService(testfidl::TestInterface::Name_);
 
-  auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
+  auto stub = filtered_client_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&stub, ZX_OK);
 }
 
@@ -39,15 +40,15 @@
 TEST_F(FilteredServiceDirectoryTest, ConnectMultiple) {
   filtered_service_directory_->AddService(testfidl::TestInterface::Name_);
 
-  auto stub1 = filtered_client_->ConnectToService<testfidl::TestInterface>();
-  auto stub2 = filtered_client_->ConnectToService<testfidl::TestInterface>();
+  auto stub1 = filtered_client_->Connect<testfidl::TestInterface>();
+  auto stub2 = filtered_client_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&stub1, ZX_OK);
   VerifyTestInterface(&stub2, ZX_OK);
 }
 
 // Verify that non-whitelisted services are blocked.
 TEST_F(FilteredServiceDirectoryTest, ServiceBlocked) {
-  auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
+  auto stub = filtered_client_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
 }
 
@@ -58,7 +59,7 @@
 
   service_binding_.reset();
 
-  auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
+  auto stub = filtered_client_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
 }
 
@@ -68,9 +69,9 @@
   filtered_service_directory_->AddService(testfidl::TestInterface::Name_);
 
   service_binding_.reset();
-  service_directory_.reset();
+  outgoing_directory_.reset();
 
-  auto stub = filtered_client_->ConnectToService<testfidl::TestInterface>();
+  auto stub = filtered_client_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
 }
 
diff --git a/base/fuchsia/scoped_service_binding.h b/base/fuchsia/scoped_service_binding.h
index efbd7346..83065658 100644
--- a/base/fuchsia/scoped_service_binding.h
+++ b/base/fuchsia/scoped_service_binding.h
@@ -6,8 +6,10 @@
 #define BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_
 
 #include <lib/fidl/cpp/binding_set.h>
+#include <lib/sys/cpp/outgoing_directory.h>
 
 #include "base/bind.h"
+#include "base/callback.h"
 #include "base/fuchsia/service_directory.h"
 
 namespace base {
@@ -16,14 +18,41 @@
 template <typename Interface>
 class ScopedServiceBinding {
  public:
-  // |service_directory| and |impl| must outlive the binding.
-  ScopedServiceBinding(ServiceDirectory* service_directory, Interface* impl)
-      : directory_(service_directory), impl_(impl) {
-    directory_->AddService(
-        BindRepeating(&ScopedServiceBinding::BindClient, Unretained(this)));
+  // Published a public service in the specified |outgoing_directory|.
+  // |outgoing_directory| and |impl| must outlive the binding.
+  ScopedServiceBinding(sys::OutgoingDirectory* outgoing_directory,
+                       Interface* impl)
+      : directory_(outgoing_directory), impl_(impl) {
+    directory_->AddPublicService<Interface>(
+        [this](fidl::InterfaceRequest<Interface> request) {
+          BindClient(std::move(request));
+        });
   }
 
-  ~ScopedServiceBinding() { directory_->RemoveService(Interface::Name_); }
+  // Publishes a service in the specified |pseudo_dir|. |pseudo_dir| and |impl|
+  // must outlive the binding.
+  ScopedServiceBinding(vfs::PseudoDir* pseudo_dir, Interface* impl)
+      : pseudo_dir_(pseudo_dir), impl_(impl) {
+    pseudo_dir_->AddEntry(
+        Interface::Name_,
+        std::make_unique<vfs::Service>(fidl::InterfaceRequestHandler<Interface>(
+            [this](fidl::InterfaceRequest<Interface> request) {
+              BindClient(std::move(request));
+            })));
+  }
+
+  // TODO(crbug.com/974072): Remove this constructor once all code has been
+  // migrated from base::fuchsia::ServiceDirectory to sys::OutgoingDirectory.
+  ScopedServiceBinding(ServiceDirectory* service_directory, Interface* impl)
+      : ScopedServiceBinding(service_directory->outgoing_directory(), impl) {}
+
+  ~ScopedServiceBinding() {
+    if (directory_) {
+      directory_->RemovePublicService<Interface>();
+    } else {
+      pseudo_dir_->RemoveEntry(Interface::Name_);
+    }
+  }
 
   void SetOnLastClientCallback(base::OnceClosure on_last_client_callback) {
     on_last_client_callback_ = std::move(on_last_client_callback);
@@ -43,7 +72,8 @@
     std::move(on_last_client_callback_).Run();
   }
 
-  ServiceDirectory* const directory_;
+  sys::OutgoingDirectory* const directory_ = nullptr;
+  vfs::PseudoDir* const pseudo_dir_ = nullptr;
   Interface* const impl_;
   fidl::BindingSet<Interface> bindings_;
   base::OnceClosure on_last_client_callback_;
@@ -60,16 +90,26 @@
               ScopedServiceBindingPolicy::kPreferNew>
 class ScopedSingleClientServiceBinding {
  public:
-  // |service_directory| and |impl| must outlive the binding.
-  ScopedSingleClientServiceBinding(ServiceDirectory* service_directory,
+  // |outgoing_directory| and |impl| must outlive the binding.
+  ScopedSingleClientServiceBinding(sys::OutgoingDirectory* outgoing_directory,
                                    Interface* impl)
-      : directory_(service_directory), binding_(impl) {
-    directory_->AddService(BindRepeating(
-        &ScopedSingleClientServiceBinding::BindClient, Unretained(this)));
+      : directory_(std::move(outgoing_directory)), binding_(impl) {
+    directory_->AddPublicService<Interface>(
+        [this](fidl::InterfaceRequest<Interface> request) {
+          BindClient(std::move(request));
+        });
   }
 
+  // TODO(crbug.com/974072): Remove this constructor once all code has been
+  // migrated from base::fuchsia::ServiceDirectory to sys::OutgoingDirectory.
+  ScopedSingleClientServiceBinding(ServiceDirectory* service_directory,
+                                   Interface* impl)
+      : ScopedSingleClientServiceBinding(
+            service_directory->outgoing_directory(),
+            impl) {}
+
   ~ScopedSingleClientServiceBinding() {
-    directory_->RemoveService(Interface::Name_);
+    directory_->RemovePublicService<Interface>();
   }
 
   typename Interface::EventSender_& events() { return binding_.events(); }
@@ -85,8 +125,9 @@
  private:
   void BindClient(fidl::InterfaceRequest<Interface> request) {
     if (Policy == ScopedServiceBindingPolicy::kPreferExisting &&
-        binding_.is_bound())
+        binding_.is_bound()) {
       return;
+    }
     binding_.Bind(std::move(request));
   }
 
@@ -95,7 +136,7 @@
     std::move(on_last_client_callback_).Run();
   }
 
-  ServiceDirectory* const directory_;
+  sys::OutgoingDirectory* const directory_;
   fidl::Binding<Interface> binding_;
   base::OnceClosure on_last_client_callback_;
 
diff --git a/base/fuchsia/scoped_service_binding_unittest.cc b/base/fuchsia/scoped_service_binding_unittest.cc
index 3858ae2b..ddbea03 100644
--- a/base/fuchsia/scoped_service_binding_unittest.cc
+++ b/base/fuchsia/scoped_service_binding_unittest.cc
@@ -15,10 +15,8 @@
 
 // Verifies that ScopedServiceBinding allows connection more than once.
 TEST_F(ScopedServiceBindingTest, ConnectTwice) {
-  auto stub = public_service_directory_client_
-                  ->ConnectToService<testfidl::TestInterface>();
-  auto stub2 = public_service_directory_client_
-                   ->ConnectToService<testfidl::TestInterface>();
+  auto stub = public_service_directory_->Connect<testfidl::TestInterface>();
+  auto stub2 = public_service_directory_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&stub, ZX_OK);
   VerifyTestInterface(&stub2, ZX_OK);
 }
@@ -30,17 +28,17 @@
   service_binding_ = nullptr;
   ScopedSingleClientServiceBinding<testfidl::TestInterface,
                                    ScopedServiceBindingPolicy::kPreferNew>
-      binding(service_directory_.get(), &test_service_);
+      binding(outgoing_directory_.get(), &test_service_);
 
   // Connect the first client, and verify that it is functional.
-  auto existing_client = public_service_directory_client_
-                             ->ConnectToService<testfidl::TestInterface>();
+  auto existing_client =
+      public_service_directory_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&existing_client, ZX_OK);
 
   // Connect the second client, so the existing one should be disconnected and
   // the new should be functional.
-  auto new_client = public_service_directory_client_
-                        ->ConnectToService<testfidl::TestInterface>();
+  auto new_client =
+      public_service_directory_->Connect<testfidl::TestInterface>();
   RunLoop().RunUntilIdle();
   EXPECT_FALSE(existing_client);
   VerifyTestInterface(&new_client, ZX_OK);
@@ -53,17 +51,17 @@
   service_binding_ = nullptr;
   ScopedSingleClientServiceBinding<testfidl::TestInterface,
                                    ScopedServiceBindingPolicy::kPreferExisting>
-      binding(service_directory_.get(), &test_service_);
+      binding(outgoing_directory_.get(), &test_service_);
 
   // Connect the first client, and verify that it is functional.
-  auto existing_client = public_service_directory_client_
-                             ->ConnectToService<testfidl::TestInterface>();
+  auto existing_client =
+      public_service_directory_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&existing_client, ZX_OK);
 
   // Connect the second client, then verify that the it gets closed and the
   // existing one remains functional.
-  auto new_client = public_service_directory_client_
-                        ->ConnectToService<testfidl::TestInterface>();
+  auto new_client =
+      public_service_directory_->Connect<testfidl::TestInterface>();
   RunLoop().RunUntilIdle();
   EXPECT_FALSE(new_client);
   VerifyTestInterface(&existing_client, ZX_OK);
@@ -74,21 +72,39 @@
   // Teardown the default multi-client binding and create a prefer-new one.
   service_binding_ = nullptr;
   ScopedSingleClientServiceBinding<testfidl::TestInterface> binding(
-      service_directory_.get(), &test_service_);
+      outgoing_directory_.get(), &test_service_);
 
   // Connect the first client, and verify that it is functional.
-  auto existing_client = public_service_directory_client_
-                             ->ConnectToService<testfidl::TestInterface>();
+  auto existing_client =
+      public_service_directory_->Connect<testfidl::TestInterface>();
   VerifyTestInterface(&existing_client, ZX_OK);
 
   // Connect the second client, so the existing one should be disconnected and
   // the new should be functional.
-  auto new_client = public_service_directory_client_
-                        ->ConnectToService<testfidl::TestInterface>();
+  auto new_client =
+      public_service_directory_->Connect<testfidl::TestInterface>();
   RunLoop().RunUntilIdle();
   EXPECT_FALSE(existing_client);
   VerifyTestInterface(&new_client, ZX_OK);
 }
 
+// Verify that we can publish a debug service.
+TEST_F(ScopedServiceBindingTest, ConnectDebugService) {
+  // Remove the public service binding.
+  service_binding_.reset();
+
+  // Publish the test service to the "debug" directory.
+  ScopedServiceBinding<testfidl::TestInterface> debug_service_binding(
+      outgoing_directory_->debug_dir(), &test_service_);
+
+  auto debug_stub =
+      debug_service_directory_->Connect<testfidl::TestInterface>();
+  VerifyTestInterface(&debug_stub, ZX_OK);
+
+  auto release_stub =
+      public_service_directory_->Connect<testfidl::TestInterface>();
+  VerifyTestInterface(&release_stub, ZX_ERR_PEER_CLOSED);
+}
+
 }  // namespace fuchsia
 }  // namespace base
diff --git a/base/fuchsia/service_directory.cc b/base/fuchsia/service_directory.cc
index ae21911..0b84904 100644
--- a/base/fuchsia/service_directory.cc
+++ b/base/fuchsia/service_directory.cc
@@ -4,11 +4,10 @@
 
 #include "base/fuchsia/service_directory.h"
 
-#include <lib/async/default.h>
-#include <lib/svc/dir.h>
-#include <zircon/process.h>
-#include <zircon/processargs.h>
+#include <lib/sys/cpp/component_context.h>
+#include <lib/sys/cpp/outgoing_directory.h>
 
+#include "base/fuchsia/default_context.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop_current.h"
@@ -22,128 +21,27 @@
   Initialize(std::move(request));
 }
 
+ServiceDirectory::ServiceDirectory(sys::OutgoingDirectory* directory)
+    : directory_(directory) {}
+
 ServiceDirectory::ServiceDirectory() = default;
-
-ServiceDirectory::~ServiceDirectory() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(services_.empty());
-
-  // Only the root ServiceDirectory "owns" svc_dir_.
-  if (!sub_directory_) {
-    zx_status_t status = svc_dir_destroy(svc_dir_);
-    ZX_DCHECK(status == ZX_OK, status);
-  }
-}
+ServiceDirectory::~ServiceDirectory() = default;
 
 // static
 ServiceDirectory* ServiceDirectory::GetDefault() {
   static NoDestructor<ServiceDirectory> directory(
-      fidl::InterfaceRequest<::fuchsia::io::Directory>(
-          zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST))));
+      ComponentContextForCurrentProcess()->outgoing().get());
   return directory.get();
 }
 
 void ServiceDirectory::Initialize(
     fidl::InterfaceRequest<::fuchsia::io::Directory> request) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(!svc_dir_);
-
-  zx_status_t status =
-      svc_dir_create(async_get_default_dispatcher(),
-                     request.TakeChannel().release(), &svc_dir_);
-  ZX_CHECK(status == ZX_OK, status);
-
-  debug_ = WrapUnique(new ServiceDirectory(svc_dir_, "debug"));
-}
-
-void ServiceDirectory::AddServiceUnsafe(
-    StringPiece name,
-    RepeatingCallback<void(zx::channel)> connect_callback) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(svc_dir_);
-  DCHECK(services_.find(name) == services_.end());
-
-  std::string name_str = name.as_string();
-  services_[name_str] = connect_callback;
-
-  if (sub_directory_) {
-    zx_status_t status =
-        svc_dir_add_service(svc_dir_, sub_directory_, name_str.c_str(), this,
-                            &ServiceDirectory::HandleConnectRequest);
-    ZX_DCHECK(status == ZX_OK, status);
-  } else {
-    // Publish to "svc".
-    zx_status_t status =
-        svc_dir_add_service(svc_dir_, "svc", name_str.c_str(), this,
-                            &ServiceDirectory::HandleConnectRequest);
-    ZX_DCHECK(status == ZX_OK, status);
-
-    // Publish to "public" for compatibility.
-    status = svc_dir_add_service(svc_dir_, "public", name_str.c_str(), this,
-                                 &ServiceDirectory::HandleConnectRequest);
-    ZX_DCHECK(status == ZX_OK, status);
-
-    // Publish to the legacy "flat" namespace, which is required by some
-    // clients.
-    status = svc_dir_add_service(svc_dir_, nullptr, name_str.c_str(), this,
-                                 &ServiceDirectory::HandleConnectRequest);
-    ZX_DCHECK(status == ZX_OK, status);
-  }
-}
-
-void ServiceDirectory::RemoveService(StringPiece name) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(svc_dir_);
-
-  std::string name_str = name.as_string();
-
-  auto it = services_.find(name_str);
-  DCHECK(it != services_.end());
-  services_.erase(it);
-
-  if (sub_directory_) {
-    zx_status_t status =
-        svc_dir_remove_service(svc_dir_, sub_directory_, name_str.c_str());
-    ZX_DCHECK(status == ZX_OK, status);
-  } else {
-    // Unregister from "svc", "public", and flat namespace.
-    zx_status_t status =
-        svc_dir_remove_service(svc_dir_, "svc", name_str.c_str());
-    ZX_DCHECK(status == ZX_OK, status);
-    status = svc_dir_remove_service(svc_dir_, "public", name_str.c_str());
-    ZX_DCHECK(status == ZX_OK, status);
-    status = svc_dir_remove_service(svc_dir_, nullptr, name_str.c_str());
-    ZX_DCHECK(status == ZX_OK, status);
-  }
-}
-
-void ServiceDirectory::RemoveAllServices() {
-  while (!services_.empty()) {
-    RemoveService(services_.begin()->first);
-  }
-}
-
-// static
-void ServiceDirectory::HandleConnectRequest(void* context,
-                                            const char* service_name,
-                                            zx_handle_t service_request) {
-  auto* directory = reinterpret_cast<ServiceDirectory*>(context);
-  DCHECK_CALLED_ON_VALID_THREAD(directory->thread_checker_);
-
-  auto it = directory->services_.find(service_name);
-
-  // HandleConnectRequest() is expected to be called only for registered
-  // services.
-  DCHECK(it != directory->services_.end());
-
-  it->second.Run(zx::channel(service_request));
-}
-
-ServiceDirectory::ServiceDirectory(svc_dir_t* svc_dir, const char* name) {
-  DCHECK(svc_dir);
-
-  svc_dir_ = svc_dir;
-  sub_directory_ = name;
+  DCHECK(!owned_directory_);
+  owned_directory_ = std::make_unique<sys::OutgoingDirectory>();
+  directory_ = owned_directory_.get();
+  directory_->GetOrCreateDirectory("svc")->Serve(
+      ::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
+      request.TakeChannel());
 }
 
 }  // namespace fuchsia
diff --git a/base/fuchsia/service_directory.h b/base/fuchsia/service_directory.h
index 293efdcf..e5f9568 100644
--- a/base/fuchsia/service_directory.h
+++ b/base/fuchsia/service_directory.h
@@ -17,9 +17,10 @@
 #include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
-#include "base/threading/thread_checker.h"
 
-typedef struct svc_dir svc_dir_t;
+namespace sys {
+class OutgoingDirectory;
+}  // namespace sys
 
 namespace base {
 namespace fuchsia {
@@ -34,14 +35,19 @@
 // Debug services are published to a "debug" sub-directory only accessible by
 // other services via the Hub.
 //
-// Not thread-safe. All methods must be called on the thread that created the
-// object.
+// TODO(crbug.com/974072): Currently this class is just a wrapper around
+// sys::OutgoingDirectory. Migrate all code to use sys::OutgoingDirectory and
+// remove this class.
 class BASE_EXPORT ServiceDirectory {
  public:
   // Responds to service requests over the supplied |request| channel.
   explicit ServiceDirectory(
       fidl::InterfaceRequest<::fuchsia::io::Directory> request);
 
+  // Wraps a sys::OutgoingDirectory. The |directory| must outlive
+  // the ServiceDirectory object.
+  explicit ServiceDirectory(sys::OutgoingDirectory* directory);
+
   // Creates an uninitialized ServiceDirectory instance. Initialize must be
   // called on the instance before any services can be registered. Unless you
   // need separate construction & initialization for a ServiceDirectory member,
@@ -58,51 +64,11 @@
   // supplied |directory_request| channel.
   void Initialize(fidl::InterfaceRequest<::fuchsia::io::Directory> request);
 
-  template <typename Interface>
-  void AddService(RepeatingCallback<void(fidl::InterfaceRequest<Interface>)>
-                      connect_callback) {
-    AddServiceUnsafe(
-        Interface::Name_,
-        BindRepeating(
-            [](decltype(connect_callback) callback, zx::channel request) {
-              callback.Run(
-                  fidl::InterfaceRequest<Interface>(std::move(request)));
-            },
-            connect_callback));
-  }
-  void RemoveService(StringPiece name);
-  void RemoveAllServices();
-
-  // Returns the debug ServiceDirectory.
-  ServiceDirectory* debug() const { return debug_.get(); }
-
-  // Passes requests for |name| through to a generic |connect_callback|.
-  // This is used only when proxying requests for interfaces not known at
-  // compile-time. Use the type-safe APIs above whenever possible.
-  void AddServiceUnsafe(StringPiece name,
-                        RepeatingCallback<void(zx::channel)> connect_callback);
+  sys::OutgoingDirectory* outgoing_directory() { return directory_; }
 
  private:
-  // Sub-directory constructor.
-  ServiceDirectory(svc_dir_t* svc_dir, const char* name);
-
-  // Called by |svc_dir_| to handle service requests.
-  static void HandleConnectRequest(void* context,
-                                   const char* service_name,
-                                   zx_handle_t service_request);
-
-  THREAD_CHECKER(thread_checker_);
-
-  // Owned by the root directory.
-  svc_dir_t* svc_dir_ = nullptr;
-  flat_map<std::string, RepeatingCallback<void(zx::channel)>> services_;
-
-  // The debug sub-directory. Empty if this is a sub-directory.
-  std::unique_ptr<ServiceDirectory> debug_;
-
-  // If mon-null, this directory represents a sub-directory of the root
-  // ServiceDirectory.
-  const char* sub_directory_ = nullptr;
+  std::unique_ptr<sys::OutgoingDirectory> owned_directory_;
+  sys::OutgoingDirectory* directory_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceDirectory);
 };
diff --git a/base/fuchsia/service_directory_test_base.cc b/base/fuchsia/service_directory_test_base.cc
index 050e1a13..5668728 100644
--- a/base/fuchsia/service_directory_test_base.cc
+++ b/base/fuchsia/service_directory_test_base.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/fuchsia/fuchsia_logging.h"
 #include "base/test/test_timeouts.h"
 
 namespace base {
@@ -17,48 +18,37 @@
     : run_timeout_(TestTimeouts::action_timeout(), BindRepeating([]() {
                      ADD_FAILURE() << "Run() timed out.";
                    })) {
-  // TODO(https://crbug.com/920920): Remove the ServiceDirectory's implicit
-  // "public" sub-directory and update this setup logic.
-
   // Mount service dir and publish the service.
+  outgoing_directory_ = std::make_unique<sys::OutgoingDirectory>();
   fidl::InterfaceHandle<::fuchsia::io::Directory> directory;
-  service_directory_ =
-      std::make_unique<ServiceDirectory>(directory.NewRequest());
+  zx_status_t status =
+      outgoing_directory_->Serve(directory.NewRequest().TakeChannel());
+  ZX_CHECK(status == ZX_OK, status);
   service_binding_ =
       std::make_unique<ScopedServiceBinding<testfidl::TestInterface>>(
-          service_directory_.get(), &test_service_);
+          outgoing_directory_.get(), &test_service_);
 
-  // Create the ServiceDirectoryClient, connected to the "svc" sub-directory.
+  // Create the sys::ServiceDirectory, connected to the "svc" sub-directory.
   fidl::InterfaceHandle<::fuchsia::io::Directory> svc_directory;
   CHECK_EQ(fdio_service_connect_at(
-               directory.channel().get(), "/svc/.",
+               directory.channel().get(), "svc",
                svc_directory.NewRequest().TakeChannel().release()),
            ZX_OK);
-  public_service_directory_client_ =
-      std::make_unique<ServiceDirectoryClient>(std::move(svc_directory));
+  public_service_directory_ =
+      std::make_unique<sys::ServiceDirectory>(std::move(svc_directory));
 
-  // Create the ServiceDirectoryClient, connected to the "debug" sub-directory.
+  // Create the sys::ServiceDirectory, connected to the "debug" sub-directory.
   fidl::InterfaceHandle<::fuchsia::io::Directory> debug_directory;
   CHECK_EQ(fdio_service_connect_at(
-               directory.channel().get(), "/debug/.",
+               directory.channel().get(), "debug",
                debug_directory.NewRequest().TakeChannel().release()),
            ZX_OK);
-  debug_service_directory_client_ =
-      std::make_unique<ServiceDirectoryClient>(std::move(debug_directory));
+  debug_service_directory_ =
+      std::make_unique<sys::ServiceDirectory>(std::move(debug_directory));
 
-  // Create the ServiceDirectoryClient, connected to the "public" sub-directory
-  // (same contents as "svc", provided for compatibility).
-  fidl::InterfaceHandle<::fuchsia::io::Directory> public_directory;
-  CHECK_EQ(fdio_service_connect_at(
-               directory.channel().get(), "/public/.",
-               public_directory.NewRequest().TakeChannel().release()),
-           ZX_OK);
-  legacy_public_service_directory_client_ =
-      std::make_unique<ServiceDirectoryClient>(std::move(public_directory));
-
-  // Create a ServiceDirectoryClient for the "private" part of the directory.
-  root_service_directory_client_ =
-      std::make_unique<ServiceDirectoryClient>(std::move(directory));
+  // Create a sys::ServiceDirectory for the "private" part of the directory.
+  root_service_directory_ =
+      std::make_unique<sys::ServiceDirectory>(std::move(directory));
 }
 
 ServiceDirectoryTestBase::~ServiceDirectoryTestBase() = default;
diff --git a/base/fuchsia/service_directory_test_base.h b/base/fuchsia/service_directory_test_base.h
index 900b008..34a7ccb8d 100644
--- a/base/fuchsia/service_directory_test_base.h
+++ b/base/fuchsia/service_directory_test_base.h
@@ -5,11 +5,12 @@
 #ifndef BASE_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_
 #define BASE_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_
 
+#include <lib/sys/cpp/outgoing_directory.h>
+#include <lib/sys/cpp/service_directory.h>
 #include <zircon/types.h>
 #include <memory>
 
 #include "base/fuchsia/scoped_service_binding.h"
-#include "base/fuchsia/service_directory_client.h"
 #include "base/fuchsia/test_interface_impl.h"
 #include "base/fuchsia/testfidl/cpp/fidl.h"
 #include "base/message_loop/message_loop.h"
@@ -32,16 +33,14 @@
 
   MessageLoopForIO message_loop_;
 
-  std::unique_ptr<ServiceDirectory> service_directory_;
+  std::unique_ptr<sys::OutgoingDirectory> outgoing_directory_;
   TestInterfaceImpl test_service_;
   std::unique_ptr<ScopedServiceBinding<testfidl::TestInterface>>
       service_binding_;
 
-  std::unique_ptr<ServiceDirectoryClient> public_service_directory_client_;
-  std::unique_ptr<ServiceDirectoryClient> debug_service_directory_client_;
-  std::unique_ptr<ServiceDirectoryClient>
-      legacy_public_service_directory_client_;
-  std::unique_ptr<ServiceDirectoryClient> root_service_directory_client_;
+  std::unique_ptr<sys::ServiceDirectory> public_service_directory_;
+  std::unique_ptr<sys::ServiceDirectory> debug_service_directory_;
+  std::unique_ptr<sys::ServiceDirectory> root_service_directory_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceDirectoryTestBase);
 };
diff --git a/base/fuchsia/service_directory_unittest.cc b/base/fuchsia/service_directory_unittest.cc
deleted file mode 100644
index 505ad7d..0000000
--- a/base/fuchsia/service_directory_unittest.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/fuchsia/service_directory.h"
-
-#include <utility>
-
-#include "base/fuchsia/service_directory_test_base.h"
-#include "base/run_loop.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace fuchsia {
-
-class ServiceDirectoryTest : public ServiceDirectoryTestBase {};
-
-// Verifies that ServiceDirectoryClient can consume a public service in
-// ServiceDirectory and that connection is disconnected when the client stub is
-// destroyed.
-TEST_F(ServiceDirectoryTest, ConnectDisconnect) {
-  auto stub = public_service_directory_client_
-                  ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&stub, ZX_OK);
-
-  RunLoop run_loop;
-  service_binding_->SetOnLastClientCallback(run_loop.QuitClosure());
-
-  stub.Unbind();
-  run_loop.Run();
-}
-
-// Verify that we can connect to a service through both "public" and "svc".
-TEST_F(ServiceDirectoryTest, ConnectNewAndLegacyServices) {
-  auto stub = public_service_directory_client_
-                  ->ConnectToService<testfidl::TestInterface>();
-  auto stub2 = legacy_public_service_directory_client_
-                   ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&stub, ZX_OK);
-  VerifyTestInterface(&stub2, ZX_OK);
-}
-
-// Verify that we can connect to the same service more than once.
-TEST_F(ServiceDirectoryTest, ConnectMulti) {
-  auto stub = public_service_directory_client_
-                  ->ConnectToService<testfidl::TestInterface>();
-  auto stub2 = public_service_directory_client_
-                   ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&stub, ZX_OK);
-  VerifyTestInterface(&stub2, ZX_OK);
-}
-
-// Verify that services are also exported to the legacy flat service namespace.
-TEST_F(ServiceDirectoryTest, ConnectLegacy) {
-  auto stub = root_service_directory_client_
-                  ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&stub, ZX_OK);
-}
-
-// Verify that ServiceDirectoryClient can handle the case when the service
-// directory connection is disconnected.
-TEST_F(ServiceDirectoryTest, DirectoryGone) {
-  service_binding_.reset();
-  service_directory_.reset();
-
-  fidl::InterfacePtr<testfidl::TestInterface> stub;
-  zx_status_t status =
-      public_service_directory_client_->ConnectToService(stub.NewRequest());
-  EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
-
-  VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
-}
-
-// Verify that the case when the service doesn't exist is handled properly.
-TEST_F(ServiceDirectoryTest, NoService) {
-  service_binding_.reset();
-  auto stub = public_service_directory_client_
-                  ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
-}
-
-// Verify that we can connect to a debug service.
-TEST_F(ServiceDirectoryTest, ConnectDebugService) {
-  // Remove the public service binding.
-  service_binding_.reset();
-
-  // Publish the test service to the "debug" directory.
-  ScopedServiceBinding<testfidl::TestInterface> debug_service_binding(
-      service_directory_->debug(), &test_service_);
-
-  auto debug_stub = debug_service_directory_client_
-                        ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&debug_stub, ZX_OK);
-
-  auto release_stub = public_service_directory_client_
-                          ->ConnectToService<testfidl::TestInterface>();
-  VerifyTestInterface(&release_stub, ZX_ERR_PEER_CLOSED);
-}
-
-}  // namespace fuchsia
-}  // namespace base
diff --git a/base/fuchsia/service_provider_impl.cc b/base/fuchsia/service_provider_impl.cc
index c21ce6c1..936ff20 100644
--- a/base/fuchsia/service_provider_impl.cc
+++ b/base/fuchsia/service_provider_impl.cc
@@ -4,11 +4,23 @@
 
 #include "base/fuchsia/service_provider_impl.h"
 
+#include <lib/sys/cpp/outgoing_directory.h>
 #include <utility>
 
 namespace base {
 namespace fuchsia {
 
+// static
+std::unique_ptr<ServiceProviderImpl>
+ServiceProviderImpl::CreateForOutgoingDirectory(
+    sys::OutgoingDirectory* outgoing_directory) {
+  fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory;
+  outgoing_directory->GetOrCreateDirectory("svc")->Serve(
+      ::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
+      service_directory.NewRequest().TakeChannel());
+  return std::make_unique<ServiceProviderImpl>(std::move(service_directory));
+}
+
 ServiceProviderImpl::ServiceProviderImpl(
     fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory)
     : directory_(std::move(service_directory)) {}
diff --git a/base/fuchsia/service_provider_impl.h b/base/fuchsia/service_provider_impl.h
index 186b5d1d..99a70ae 100644
--- a/base/fuchsia/service_provider_impl.h
+++ b/base/fuchsia/service_provider_impl.h
@@ -17,6 +17,10 @@
 #include "base/fuchsia/service_directory_client.h"
 #include "base/macros.h"
 
+namespace sys {
+class OutgoingDirectory;
+}  // namespace sys
+
 namespace base {
 namespace fuchsia {
 
@@ -25,6 +29,11 @@
 // TODO(https://crbug.com/920920): Remove this when ServiceProvider is gone.
 class BASE_EXPORT ServiceProviderImpl : public ::fuchsia::sys::ServiceProvider {
  public:
+  // Constructor that creates ServiceProvider for public services in the
+  // specified OutgoingDirectory.
+  static std::unique_ptr<ServiceProviderImpl> CreateForOutgoingDirectory(
+      sys::OutgoingDirectory* outgoing_directory);
+
   explicit ServiceProviderImpl(
       fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory);
   ~ServiceProviderImpl() override;
diff --git a/base/fuchsia/service_provider_impl_unittest.cc b/base/fuchsia/service_provider_impl_unittest.cc
index 4a36b30..116c638 100644
--- a/base/fuchsia/service_provider_impl_unittest.cc
+++ b/base/fuchsia/service_provider_impl_unittest.cc
@@ -4,11 +4,11 @@
 
 #include "base/fuchsia/service_provider_impl.h"
 
+#include <lib/sys/cpp/outgoing_directory.h>
 #include <lib/zx/channel.h>
 #include <utility>
 
 #include "base/fuchsia/scoped_service_binding.h"
-#include "base/fuchsia/service_directory.h"
 #include "base/fuchsia/test_interface_impl.h"
 #include "base/fuchsia/testfidl/cpp/fidl.h"
 #include "base/message_loop/message_loop.h"
@@ -20,7 +20,12 @@
 
 class ServiceProviderImplTest : public testing::Test {
  public:
-  ServiceProviderImplTest() = default;
+  ServiceProviderImplTest() {
+    provider_impl_ =
+        ServiceProviderImpl::CreateForOutgoingDirectory(&service_directory_);
+    provider_impl_->AddBinding(provider_client_.NewRequest());
+  }
+
   ~ServiceProviderImplTest() override = default;
 
   void VerifyTestInterface(fidl::InterfacePtr<testfidl::TestInterface>* stub,
@@ -52,27 +57,25 @@
   MessageLoopForIO message_loop_;
   TestInterfaceImpl test_service_;
 
+  sys::OutgoingDirectory service_directory_;
+  std::unique_ptr<ServiceProviderImpl> provider_impl_;
+  ::fuchsia::sys::ServiceProviderPtr provider_client_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceProviderImplTest);
 };
 
-// Verifies that we can connect to the service service more than once.
+// Verifies that we can connect to the service more than once.
 TEST_F(ServiceProviderImplTest, ConnectMulti) {
-  fidl::InterfaceHandle<::fuchsia::io::Directory> directory_channel;
-  ServiceDirectory service_directory(directory_channel.NewRequest());
-  ServiceProviderImpl provider_impl(std::move(directory_channel));
   ScopedServiceBinding<testfidl::TestInterface> service_binding(
-      &service_directory, &test_service_);
-
-  ::fuchsia::sys::ServiceProviderPtr provider_client;
-  provider_impl.AddBinding(provider_client.NewRequest());
+      &service_directory_, &test_service_);
 
   testfidl::TestInterfacePtr stub;
-  provider_client->ConnectToService(testfidl::TestInterface::Name_,
-                                    stub.NewRequest().TakeChannel());
+  provider_client_->ConnectToService(testfidl::TestInterface::Name_,
+                                     stub.NewRequest().TakeChannel());
 
   testfidl::TestInterfacePtr stub2;
-  provider_client->ConnectToService(testfidl::TestInterface::Name_,
-                                    stub2.NewRequest().TakeChannel());
+  provider_client_->ConnectToService(testfidl::TestInterface::Name_,
+                                     stub2.NewRequest().TakeChannel());
 
   VerifyTestInterface(&stub, ZX_OK);
   VerifyTestInterface(&stub2, ZX_OK);
@@ -80,16 +83,9 @@
 
 // Verify that the case when the service doesn't exist is handled properly.
 TEST_F(ServiceProviderImplTest, NoService) {
-  fidl::InterfaceHandle<::fuchsia::io::Directory> directory_channel;
-  ServiceDirectory service_directory(directory_channel.NewRequest());
-  ServiceProviderImpl provider_impl(std::move(directory_channel));
-
-  ::fuchsia::sys::ServiceProviderPtr provider_client;
-  provider_impl.AddBinding(provider_client.NewRequest());
-
   testfidl::TestInterfacePtr stub;
-  provider_client->ConnectToService(testfidl::TestInterface::Name_,
-                                    stub.NewRequest().TakeChannel());
+  provider_client_->ConnectToService(testfidl::TestInterface::Name_,
+                                     stub.NewRequest().TakeChannel());
 
   VerifyTestInterface(&stub, ZX_ERR_PEER_CLOSED);
 }
diff --git a/base/fuchsia/startup_context.cc b/base/fuchsia/startup_context.cc
index 227b42a..b1b0ebc 100644
--- a/base/fuchsia/startup_context.cc
+++ b/base/fuchsia/startup_context.cc
@@ -5,92 +5,95 @@
 #include "base/fuchsia/startup_context.h"
 
 #include <fuchsia/io/cpp/fidl.h>
+#include <lib/sys/cpp/outgoing_directory.h>
+#include <lib/sys/cpp/service_directory.h>
 
 #include "base/fuchsia/file_utils.h"
 
 namespace base {
 namespace fuchsia {
 
-StartupContext::StartupContext(::fuchsia::sys::StartupInfo startup_info)
-    : startup_info_(std::move(startup_info)) {
+StartupContext::StartupContext(::fuchsia::sys::StartupInfo startup_info) {
+  std::unique_ptr<sys::ServiceDirectory> incoming_services;
+
   // Component manager generates |flat_namespace|, so things are horribly broken
   // if |flat_namespace| is malformed.
-  CHECK_EQ(startup_info_.flat_namespace.directories.size(),
-           startup_info_.flat_namespace.paths.size());
+  CHECK_EQ(startup_info.flat_namespace.directories.size(),
+           startup_info.flat_namespace.paths.size());
 
-  // Find the /svc directory and wrap it into a ServiceDirectoryClient.
-  for (size_t i = 0; i < startup_info_.flat_namespace.paths.size(); ++i) {
-    if (startup_info_.flat_namespace.paths[i] == kServiceDirectoryPath) {
-      incoming_services_ = std::make_unique<ServiceDirectoryClient>(
-          fidl::InterfaceHandle<::fuchsia::io::Directory>(
-              std::move(startup_info_.flat_namespace.directories[i])));
+  // Find the /svc directory and wrap it into a sys::ServiceDirectory.
+  for (size_t i = 0; i < startup_info.flat_namespace.paths.size(); ++i) {
+    if (startup_info.flat_namespace.paths[i] == kServiceDirectoryPath) {
+      incoming_services = std::make_unique<sys::ServiceDirectory>(
+          std::move(startup_info.flat_namespace.directories[i]));
       break;
     }
   }
 
   // TODO(https://crbug.com/933834): Remove these workarounds when we migrate to
   // the new component manager.
-  if (!incoming_services_ && startup_info_.launch_info.flat_namespace) {
+  if (!incoming_services && startup_info.launch_info.flat_namespace) {
     LOG(WARNING) << "Falling back to LaunchInfo namespace";
     for (size_t i = 0;
-         i < startup_info_.launch_info.flat_namespace->paths.size(); ++i) {
-      if (startup_info_.launch_info.flat_namespace->paths[i] ==
+         i < startup_info.launch_info.flat_namespace->paths.size(); ++i) {
+      if (startup_info.launch_info.flat_namespace->paths[i] ==
           kServiceDirectoryPath) {
-        incoming_services_ = std::make_unique<ServiceDirectoryClient>(
-            fidl::InterfaceHandle<::fuchsia::io::Directory>(std::move(
-                startup_info_.launch_info.flat_namespace->directories[i])));
+        incoming_services = std::make_unique<sys::ServiceDirectory>(
+            std::move(startup_info.launch_info.flat_namespace->directories[i]));
         break;
       }
     }
   }
-  if (!incoming_services_ && startup_info_.launch_info.additional_services) {
+
+  if (!incoming_services && startup_info.launch_info.additional_services) {
     LOG(WARNING) << "Falling back to additional ServiceList services";
 
-    // Construct a ServiceDirectory and publish the additional services into it.
-    fidl::InterfaceHandle<::fuchsia::io::Directory> incoming_directory;
+    // Construct a OutgoingDirectory and publish the additional services into
+    // it.
     additional_services_.Bind(
-        std::move(startup_info_.launch_info.additional_services->provider));
-    additional_services_directory_ =
-        std::make_unique<ServiceDirectory>(incoming_directory.NewRequest());
-    for (auto& name : startup_info_.launch_info.additional_services->names) {
-      additional_services_directory_->AddServiceUnsafe(
-          name, base::BindRepeating(
-                    &::fuchsia::sys::ServiceProvider::ConnectToService,
-                    base::Unretained(additional_services_.get()), name));
+        std::move(startup_info.launch_info.additional_services->provider));
+    additional_services_directory_ = std::make_unique<sys::OutgoingDirectory>();
+    for (auto& name : startup_info.launch_info.additional_services->names) {
+      additional_services_directory_->AddPublicService(
+          std::make_unique<vfs::Service>([this, name](
+                                             zx::channel channel,
+                                             async_dispatcher_t* dispatcher) {
+            additional_services_->ConnectToService(name, std::move(channel));
+          }),
+          name);
     }
 
-    // Publish those services to the caller as |incoming_services_|.
-    incoming_services_ = std::make_unique<ServiceDirectoryClient>(
-        fidl::InterfaceHandle<::fuchsia::io::Directory>(
-            std::move(incoming_directory)));
+    // Publish those services to the caller as |incoming_services|.
+    fidl::InterfaceHandle<::fuchsia::io::Directory> incoming_directory;
+    additional_services_directory_->GetOrCreateDirectory("svc")->Serve(
+        ::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE,
+        incoming_directory.NewRequest().TakeChannel());
+    incoming_services =
+        std::make_unique<sys::ServiceDirectory>(std::move(incoming_directory));
   }
 
-  if (!incoming_services_) {
+  if (!incoming_services) {
     LOG(WARNING) << "Component started without a service directory";
 
     // Create a dummy ServiceDirectoryClient with a channel that's not
     // connected on the other end.
     fidl::InterfaceHandle<::fuchsia::io::Directory> dummy_directory;
     ignore_result(dummy_directory.NewRequest());
-    incoming_services_ =
-        std::make_unique<ServiceDirectoryClient>(std::move(dummy_directory));
+    incoming_services =
+        std::make_unique<sys::ServiceDirectory>(std::move(dummy_directory));
   }
+
+  component_context_ = std::make_unique<sys::ComponentContext>(
+      std::move(incoming_services),
+      std::move(startup_info.launch_info.directory_request));
+
+  service_directory_ =
+      std::make_unique<ServiceDirectory>(component_context_->outgoing().get());
+  service_directory_client_ = std::make_unique<ServiceDirectoryClient>(
+      component_context_->svc()->CloneChannel());
 }
 
-StartupContext::~StartupContext() {
-  // |additional_services_directory_| needs to be empty for clean teardown.
-  if (additional_services_directory_)
-    additional_services_directory_->RemoveAllServices();
-}
-
-ServiceDirectory* StartupContext::public_services() {
-  if (!public_services_ && startup_info_.launch_info.directory_request) {
-    public_services_ = std::make_unique<ServiceDirectory>(
-        fidl::InterfaceRequest<::fuchsia::io::Directory>(
-            std::move(startup_info_.launch_info.directory_request)));
-  }
-  return public_services_.get();
-}
+StartupContext::~StartupContext() = default;
 
 }  // namespace fuchsia
 }  // namespace base
diff --git a/base/fuchsia/startup_context.h b/base/fuchsia/startup_context.h
index fa4e330..aa879fa10 100644
--- a/base/fuchsia/startup_context.h
+++ b/base/fuchsia/startup_context.h
@@ -6,6 +6,7 @@
 #define BASE_FUCHSIA_STARTUP_CONTEXT_H_
 
 #include <fuchsia/sys/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
 #include <memory>
 
 #include "base/base_export.h"
@@ -26,28 +27,32 @@
   explicit StartupContext(::fuchsia::sys::StartupInfo startup_info);
   virtual ~StartupContext();
 
-  // Returns the namespace of services published for use by the component.
-  const ServiceDirectoryClient* incoming_services() const {
-    DCHECK(incoming_services_);
-    return incoming_services_.get();
+  // Returns the ComponentContext for the current component. Note that all
+  // outgoing services should be bound immediately after the first call to this
+  // API, before returning control to the message loop, at which point we will
+  // start processing service connection requests.
+  sys::ComponentContext* component_context() const {
+    return component_context_.get();
   }
 
-  // Returns the outgoing directory into which this component binds services.
-  // Note that all services should be bound immediately after the first call to
-  // this API, before returning control to the message loop, at which point we
-  // will start processing service connection requests.
-  ServiceDirectory* public_services();
+  // TODO(crbug.com/974072): These are legacy ServiceDirectory and
+  // ServiceDirectoryClient. Remove once all clients have been migrated to
+  // sys::OutgoingDirectory and sys::ServiceDirectory.
+  ServiceDirectoryClient* incoming_services() const {
+    return service_directory_client_.get();
+  }
+  ServiceDirectory* public_services() { return service_directory_.get(); }
 
  private:
-  ::fuchsia::sys::StartupInfo startup_info_;
-
-  std::unique_ptr<ServiceDirectoryClient> incoming_services_;
-  std::unique_ptr<ServiceDirectory> public_services_;
-
   // TODO(https://crbug.com/933834): Remove these when we migrate to the new
   // component manager APIs.
   ::fuchsia::sys::ServiceProviderPtr additional_services_;
-  std::unique_ptr<ServiceDirectory> additional_services_directory_;
+  std::unique_ptr<sys::OutgoingDirectory> additional_services_directory_;
+
+  std::unique_ptr<sys::ComponentContext> component_context_;
+
+  std::unique_ptr<ServiceDirectory> service_directory_;
+  std::unique_ptr<ServiceDirectoryClient> service_directory_client_;
 
   DISALLOW_COPY_AND_ASSIGN(StartupContext);
 };
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index d9c2c58..91aef83 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -363,7 +363,19 @@
 
 BASE_EXPORT extern NSAppearanceName const NSAppearanceNameDarkAqua;
 
-#endif
+#endif  // MAC_OS_X_VERSION_10_14
+
+#if !defined(MAC_OS_X_VERSION_10_15) || \
+    MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_15
+
+@interface SFUniversalLink : NSObject
+- (instancetype)initWithWebpageURL:(NSURL*)url;
+@property(readonly) NSURL* webpageURL;
+@property(readonly) NSURL* applicationURL;
+@property(getter=isEnabled) BOOL enabled;
+@end
+
+#endif  // MAC_OS_X_VERSION_10_15
 
 // ----------------------------------------------------------------------------
 // The symbol for kCWSSIDDidChangeNotification is available in the
diff --git a/base/system/sys_info_fuchsia.cc b/base/system/sys_info_fuchsia.cc
index 092a6ae..8206aa52 100644
--- a/base/system/sys_info_fuchsia.cc
+++ b/base/system/sys_info_fuchsia.cc
@@ -4,11 +4,12 @@
 
 #include "base/system/sys_info.h"
 
-#include <sys/utsname.h>
 #include <zircon/syscalls.h>
 
+#include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -58,45 +59,34 @@
 
 // static
 std::string SysInfo::OperatingSystemVersion() {
-  struct utsname info;
-  if (uname(&info) < 0) {
-    NOTREACHED();
+  char result[64] = {};
+  zx_status_t status = zx_system_get_version(result, sizeof(result));
+  if (status != ZX_OK) {
+    ZX_DLOG(WARNING, status) << "zx_system_get_version";
     return std::string();
   }
-
-  return std::string(info.release);
+  return result;
 }
 
 // static
 void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
                                             int32_t* minor_version,
                                             int32_t* bugfix_version) {
-  struct utsname info;
-  if (uname(&info) < 0) {
-    *major_version = 0;
-    *minor_version = 0;
-    *bugfix_version = 0;
-    return;
-  }
-  int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
-                        bugfix_version);
-  if (num_read < 1)
-    *major_version = 0;
-  if (num_read < 2)
-    *minor_version = 0;
-  if (num_read < 3)
-    *bugfix_version = 0;
+  // Fuchsia doesn't have OS version numbers.
+  *major_version = 0;
+  *minor_version = 0;
+  *bugfix_version = 0;
 }
 
 // static
 std::string SysInfo::OperatingSystemArchitecture() {
-  struct utsname info;
-  if (uname(&info) < 0) {
-    NOTREACHED();
-    return std::string();
-  }
-
-  return info.machine;
+#if defined(ARCH_CPU_X86_64)
+  return "x86_64";
+#elif defined(ARCH_CPU_ARM64)
+  return "aarch64";
+#else
+#error Unsupported architecture.
+#endif
 }
 
 // static
diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc
index e4e3bee2..80efb99 100644
--- a/base/system/sys_info_unittest.cc
+++ b/base/system/sys_info_unittest.cc
@@ -115,7 +115,8 @@
   EXPECT_GT(SysInfo::AmountOfTotalDiskSpace(tmp_path), 0) << tmp_path.value();
 }
 
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
+    defined(OS_FUCHSIA)
 TEST_F(SysInfoTest, OperatingSystemVersionNumbers) {
   int32_t os_major_version = -1;
   int32_t os_minor_version = -1;
diff --git a/base/win/registry.cc b/base/win/registry.cc
index d7a1890b..7c324938 100644
--- a/base/win/registry.cc
+++ b/base/win/registry.cc
@@ -281,7 +281,7 @@
     return result;
 
   if (count == 0)
-    return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
+    return RegDeleteKeyEx(key_, name, wow64access_, 0);
 
   return ERROR_DIR_NOT_EMPTY;
 }
@@ -431,28 +431,9 @@
 }
 
 // static
-LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
-                                   const char16* lpSubKey,
-                                   REGSAM samDesired,
-                                   DWORD Reserved) {
-  typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
-
-  RegDeleteKeyExPtr reg_delete_key_ex_func =
-      reinterpret_cast<RegDeleteKeyExPtr>(
-          GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
-
-  if (reg_delete_key_ex_func)
-    return reg_delete_key_ex_func(hKey, as_wcstr(lpSubKey), samDesired,
-                                  Reserved);
-
-  // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
-  return RegDeleteKey(hKey, as_wcstr(lpSubKey));
-}
-
-// static
 LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) {
   // First, see if the key can be deleted without having to recurse.
-  LONG result = RegDeleteKeyExWrapper(root_key, name, access, 0);
+  LONG result = RegDeleteKeyEx(root_key, name, access, 0);
   if (result == ERROR_SUCCESS)
     return result;
 
@@ -497,7 +478,7 @@
   RegCloseKey(target_key);
 
   // Try again to delete the key.
-  result = RegDeleteKeyExWrapper(root_key, name, access, 0);
+  result = RegDeleteKeyEx(root_key, name, access, 0);
 
   return result;
 }
diff --git a/base/win/registry.h b/base/win/registry.h
index 4b85944..1c57fc9f 100644
--- a/base/win/registry.h
+++ b/base/win/registry.h
@@ -142,13 +142,6 @@
  private:
   class Watcher;
 
-  // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to
-  // RegDeleteKey.
-  static LONG RegDeleteKeyExWrapper(HKEY hKey,
-                                    const char16* lpSubKey,
-                                    REGSAM samDesired,
-                                    DWORD Reserved);
-
   // Recursively deletes a key and all of its subkeys.
   static LONG RegDelRecurse(HKEY root_key, const char16* name, REGSAM access);
 
diff --git a/base/win/startup_information.cc b/base/win/startup_information.cc
index 9986674..372b85e 100644
--- a/base/win/startup_information.cc
+++ b/base/win/startup_information.cc
@@ -6,83 +6,42 @@
 
 #include "base/logging.h"
 
-namespace {
-
-typedef BOOL (WINAPI *InitializeProcThreadAttributeListFunction)(
-    LPPROC_THREAD_ATTRIBUTE_LIST attribute_list,
-    DWORD attribute_count,
-    DWORD flags,
-    PSIZE_T size);
-static InitializeProcThreadAttributeListFunction
-    initialize_proc_thread_attribute_list;
-
-typedef BOOL (WINAPI *UpdateProcThreadAttributeFunction)(
-    LPPROC_THREAD_ATTRIBUTE_LIST attribute_list,
-    DWORD flags,
-    DWORD_PTR attribute,
-    PVOID value,
-    SIZE_T size,
-    PVOID previous_value,
-    PSIZE_T return_size);
-static UpdateProcThreadAttributeFunction update_proc_thread_attribute_list;
-
-typedef VOID (WINAPI *DeleteProcThreadAttributeListFunction)(
-    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList);
-static DeleteProcThreadAttributeListFunction delete_proc_thread_attribute_list;
-
-}  // namespace
-
 namespace base {
 namespace win {
 
-StartupInformation::StartupInformation() {
-  memset(&startup_info_, 0, sizeof(startup_info_));
+StartupInformation::StartupInformation() : startup_info_() {
   startup_info_.StartupInfo.cb = sizeof(startup_info_);
-
-  // Load the attribute API functions.
-  if (!initialize_proc_thread_attribute_list ||
-      !update_proc_thread_attribute_list ||
-      !delete_proc_thread_attribute_list) {
-    HMODULE module = ::GetModuleHandleW(L"kernel32.dll");
-    initialize_proc_thread_attribute_list =
-        reinterpret_cast<InitializeProcThreadAttributeListFunction>(
-            ::GetProcAddress(module, "InitializeProcThreadAttributeList"));
-    update_proc_thread_attribute_list =
-        reinterpret_cast<UpdateProcThreadAttributeFunction>(
-            ::GetProcAddress(module, "UpdateProcThreadAttribute"));
-    delete_proc_thread_attribute_list =
-        reinterpret_cast<DeleteProcThreadAttributeListFunction>(
-            ::GetProcAddress(module, "DeleteProcThreadAttributeList"));
-  }
 }
 
 StartupInformation::~StartupInformation() {
   if (startup_info_.lpAttributeList) {
-    delete_proc_thread_attribute_list(startup_info_.lpAttributeList);
-    delete [] reinterpret_cast<BYTE*>(startup_info_.lpAttributeList);
+    ::DeleteProcThreadAttributeList(startup_info_.lpAttributeList);
   }
 }
 
 bool StartupInformation::InitializeProcThreadAttributeList(
     DWORD attribute_count) {
   if (startup_info_.StartupInfo.cb != sizeof(startup_info_) ||
-      startup_info_.lpAttributeList)
+      startup_info_.lpAttributeList) {
     return false;
+  }
 
   SIZE_T size = 0;
-  initialize_proc_thread_attribute_list(NULL, attribute_count, 0, &size);
+  ::InitializeProcThreadAttributeList(nullptr, attribute_count, 0, &size);
   if (size == 0)
     return false;
 
-  startup_info_.lpAttributeList =
-      reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(new BYTE[size]);
-  if (!initialize_proc_thread_attribute_list(startup_info_.lpAttributeList,
-                                           attribute_count, 0, &size)) {
-    delete [] reinterpret_cast<BYTE*>(startup_info_.lpAttributeList);
-    startup_info_.lpAttributeList = NULL;
+  auto attribute_list = std::make_unique<char[]>(size);
+  auto* attribute_list_ptr =
+      reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(attribute_list.get());
+  if (!::InitializeProcThreadAttributeList(attribute_list_ptr, attribute_count,
+                                           0, &size)) {
     return false;
   }
 
+  attribute_list_ = std::move(attribute_list);
+  startup_info_.lpAttributeList = attribute_list_ptr;
+
   return true;
 }
 
@@ -92,8 +51,9 @@
     size_t size) {
   if (!startup_info_.lpAttributeList)
     return false;
-  return !!update_proc_thread_attribute_list(startup_info_.lpAttributeList, 0,
-                                       attribute, value, size, NULL, NULL);
+  return !!::UpdateProcThreadAttribute(startup_info_.lpAttributeList, 0,
+                                       attribute, value, size, nullptr,
+                                       nullptr);
 }
 
 }  // namespace win
diff --git a/base/win/startup_information.h b/base/win/startup_information.h
index 5b777bae..2d4edaf5 100644
--- a/base/win/startup_information.h
+++ b/base/win/startup_information.h
@@ -5,6 +5,8 @@
 #ifndef BASE_WIN_STARTUP_INFORMATION_H_
 #define BASE_WIN_STARTUP_INFORMATION_H_
 
+#include <memory>
+
 #include <windows.h>
 #include <stddef.h>
 
@@ -41,6 +43,7 @@
   }
 
  private:
+  std::unique_ptr<char[]> attribute_list_;
   STARTUPINFOEXW startup_info_;
   DISALLOW_COPY_AND_ASSIGN(StartupInformation);
 };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
index 86fe4ea..2f33354 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabCountProvider.java
@@ -203,6 +203,8 @@
     }
 
     private void updateTabCount() {
+        if (!mTabModelSelector.isTabStateInitialized()) return;
+
         final int tabCount =
                 mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().getCount();
         final boolean isIncognito = mTabModelSelector.isIncognitoSelected();
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index e065463..c36dccf 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3860.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3861.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/app-entitlements.plist b/chrome/app/app-entitlements.plist
index 1a198c5b..4a1d735 100644
--- a/chrome/app/app-entitlements.plist
+++ b/chrome/app/app-entitlements.plist
@@ -8,6 +8,8 @@
 	<array>
 		<string>${CHROMIUM_TEAM_ID}.${CHROMIUM_BUNDLE_ID}.webauthn</string>
 	</array>
+	<key>com.apple.developer.associated-domains.applinks.read-write</key>
+	<true/>
 	<key>com.apple.security.device.audio-input</key>
 	<true/>
 	<key>com.apple.security.device.bluetooth</key>
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index 899cbed..327a13d 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -159,7 +159,6 @@
         .RequireCapability("unzip", "unzip_file")
         .RequireCapability("util_win", "util_win")
         .RequireCapability("wifi_util_win", "wifi_credentials")
-        .RequireCapability("video_capture", "capture")
         .RequireCapability("xr_device_service", "xr_device_provider")
         .RequireCapability("xr_device_service", "xr_device_test_hook")
 #if defined(OS_CHROMEOS)
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 7f2d631..03768ae 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2185,7 +2185,7 @@
     <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_CHANGE" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for dialog button to change a PIN.">
       Change
     </message>
-    <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_ENTER" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for dialog button to enter a PIN.">
+    <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_ENTER" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for dialog button to enter a PIN; note that in this context, 'Enter' refers to the act of entering a PIN, not the name of a key on the keyboard." meaning="Enter a PIN into the device. [Verb]">
       Enter
     </message>
     <message name="IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_UNLOCK" desc="Settings > Internet > Network details > Lock/unlock SIM card: Label for button to unlock the SIM card.">
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_ENTER.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_ENTER.png.sha1
new file mode 100644
index 0000000..34de62e
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_SIM_BUTTON_ENTER.png.sha1
@@ -0,0 +1 @@
+7811c80c12ff964f3e4378fa7871f9b868a7844d
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d06f984..1c63a55 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2032,7 +2032,6 @@
     "//components/services/unzip/public/mojom",
     "//components/sessions",
     "//components/signin/core/browser",
-    "//components/signin/internal/identity_manager",  # TODO(974198): remove once closed
     "//components/signin/public/base:signin_buildflags",
     "//components/signin/public/identity_manager",
     "//components/signin/public/webdata",
@@ -2846,7 +2845,7 @@
       "//components/payments/content/android",
       "//components/resources:components_resources",
       "//components/send_tab_to_self",
-      "//components/signin/public/identity_manager",
+      "//components/signin/internal/identity_manager",  # see android/signin/DEPS
       "//media/mojo/clients",
       "//media/mojo/interfaces:constants",
       "//rlz:rlz_utils",
@@ -3785,6 +3784,8 @@
   if (is_mac) {
     allow_circular_includes_from += [ "//chrome/browser/apps/app_shim" ]
     sources += [
+      "apps/intent_helper/mac_apps_navigation_throttle.h",
+      "apps/intent_helper/mac_apps_navigation_throttle.mm",
       "badging/badge_manager_delegate_mac.cc",
       "badging/badge_manager_delegate_mac.h",
       "download/drag_download_item_mac.mm",
@@ -3807,6 +3808,9 @@
       "ImageCaptureCore.framework",
       "OpenGL.framework",
       "QuartzCore.framework",
+
+      # TODO(avi): When we move to the 10.15 SDK, -weak_framework SafariServices
+      # here and remove the manual framework opening code.
       "SecurityInterface.framework",
     ]
   }
diff --git a/chrome/browser/android/thin_webview/BUILD.gn b/chrome/browser/android/thin_webview/BUILD.gn
index 424d94bc..dfb1f698 100644
--- a/chrome/browser/android/thin_webview/BUILD.gn
+++ b/chrome/browser/android/thin_webview/BUILD.gn
@@ -15,22 +15,29 @@
 }
 
 android_library("java") {
-  java_files =
-      [ "java/src/org/chromium/chrome/browser/thinwebview/CompositorView.java" ]
+  java_files = [
+    "java/src/org/chromium/chrome/browser/thinwebview/CompositorView.java",
+    "java/src/org/chromium/chrome/browser/thinwebview/ThinWebView.java",
+  ]
 
   deps = [
     "//base:base_java",
+    "//content/public/android:content_java",
     "//ui/android:ui_java",
   ]
 }
 
 android_library("factory_java") {
-  java_files = [ "java/src/org/chromium/chrome/browser/thinwebview/CompositorViewFactory.java" ]
+  java_files = [
+    "java/src/org/chromium/chrome/browser/thinwebview/CompositorViewFactory.java",
+    "java/src/org/chromium/chrome/browser/thinwebview/ThinWebViewFactory.java",
+  ]
 
   deps = [
     ":java",
     "internal:internal_java",
     "//base:base_java",
+    "//content/public/android:content_java",
     "//ui/android:ui_java",
   ]
 }
diff --git a/chrome/browser/android/thin_webview/DEPS b/chrome/browser/android/thin_webview/DEPS
new file mode 100644
index 0000000..838459a9
--- /dev/null
+++ b/chrome/browser/android/thin_webview/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/public/android/java/src/org/chromium/content_public",
+]
diff --git a/chrome/browser/android/thin_webview/internal/BUILD.gn b/chrome/browser/android/thin_webview/internal/BUILD.gn
index 23d8a6b..e4257ce95 100644
--- a/chrome/browser/android/thin_webview/internal/BUILD.gn
+++ b/chrome/browser/android/thin_webview/internal/BUILD.gn
@@ -9,6 +9,8 @@
   sources = [
     "compositor_view_impl.cc",
     "compositor_view_impl.h",
+    "thin_webview.cc",
+    "thin_webview.h",
   ]
 
   deps = [
@@ -26,7 +28,10 @@
 }
 
 android_library("internal_java") {
-  java_files = [ "java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java" ]
+  java_files = [
+    "java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java",
+    "java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java",
+  ]
 
   deps = [
     "//base:base_java",
@@ -40,5 +45,6 @@
   visibility = [ ":*" ]
   sources = [
     "java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java",
+    "java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java",
   ]
 }
diff --git a/chrome/browser/android/thin_webview/internal/DEPS b/chrome/browser/android/thin_webview/internal/DEPS
index 160abc3..0e9a26b 100644
--- a/chrome/browser/android/thin_webview/internal/DEPS
+++ b/chrome/browser/android/thin_webview/internal/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+cc/layers",
+  "+content/public/android/java/src/org/chromium/content_public",
 ]
diff --git a/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java b/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java
new file mode 100644
index 0000000..444050c
--- /dev/null
+++ b/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/ThinWebViewImpl.java
@@ -0,0 +1,89 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.thinwebview.internal;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.thinwebview.CompositorView;
+import org.chromium.chrome.browser.thinwebview.ThinWebView;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * An android view backed by a {@link Surface} that is able to display a live {@link WebContents}.
+ */
+@JNINamespace("thin_webview::android")
+public class ThinWebViewImpl extends FrameLayout implements ThinWebView {
+    private final CompositorView mCompositorView;
+    private final long mNativeThinWebViewImpl;
+    private WebContents mWebContents;
+    private View mContentView;
+
+    /**
+     * Creates a {@link ThinWebViewImpl} backed by a {@link Surface}.
+     * @param context The Context to create this view.
+     * @param windowAndroid The associated {@code WindowAndroid} on which the view is to be
+     *         displayed.
+     */
+    public ThinWebViewImpl(Context context, WindowAndroid windowAndroid) {
+        super(context);
+        mCompositorView = new CompositorViewImpl(context, windowAndroid);
+
+        LayoutParams layoutParams = new LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        addView(mCompositorView.getView(), layoutParams);
+
+        mNativeThinWebViewImpl = nativeInit(mCompositorView, windowAndroid);
+    }
+
+    @Override
+    public View getView() {
+        return this;
+    }
+
+    @Override
+    public void attachWebContents(WebContents webContents, @Nullable View contentView) {
+        mWebContents = webContents;
+
+        setContentView(contentView);
+        nativeSetWebContents(mNativeThinWebViewImpl, mWebContents);
+        mWebContents.onShow();
+    }
+
+    @Override
+    public void destroy() {
+        mCompositorView.destroy();
+        if (mNativeThinWebViewImpl != 0) nativeDestroy(mNativeThinWebViewImpl);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        if (w != oldw || h != oldh) {
+            nativeSizeChanged(mNativeThinWebViewImpl, w, h);
+        }
+    }
+
+    private void setContentView(View contentView) {
+        if (mContentView == contentView) return;
+
+        if (mContentView != null) {
+            assert getChildCount() > 1;
+            removeViewAt(1);
+        }
+
+        mContentView = contentView;
+        if (mContentView != null) addView(mContentView, 1);
+    }
+
+    private native long nativeInit(CompositorView compositorView, WindowAndroid windowAndroid);
+    private native void nativeDestroy(long nativeThinWebView);
+    private native void nativeSetWebContents(long nativeThinWebView, WebContents webContents);
+    private native void nativeSizeChanged(long nativeThinWebView, int width, int height);
+}
diff --git a/chrome/browser/android/thin_webview/internal/thin_webview.cc b/chrome/browser/android/thin_webview/internal/thin_webview.cc
new file mode 100644
index 0000000..6c1ee541
--- /dev/null
+++ b/chrome/browser/android/thin_webview/internal/thin_webview.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/thin_webview/internal/thin_webview.h"
+
+#include "base/android/jni_android.h"
+#include "cc/layers/layer.h"
+#include "chrome/browser/android/thin_webview/internal/jni_headers/ThinWebViewImpl_jni.h"
+#include "content/public/browser/web_contents.h"
+
+using base::android::JavaParamRef;
+
+namespace thin_webview {
+namespace android {
+
+jlong JNI_ThinWebViewImpl_Init(JNIEnv* env,
+                               const JavaParamRef<jobject>& obj,
+                               const JavaParamRef<jobject>& jcompositor_view,
+                               const JavaParamRef<jobject>& jwindow_android) {
+  CompositorView* compositor_view =
+      CompositorViewImpl::FromJavaObject(jcompositor_view);
+  ui::WindowAndroid* window_android =
+      ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android);
+  ThinWebView* view =
+      new ThinWebView(env, obj, compositor_view, window_android);
+  return reinterpret_cast<intptr_t>(view);
+}
+
+ThinWebView::ThinWebView(JNIEnv* env,
+                         jobject obj,
+                         CompositorView* compositor_view,
+                         ui::WindowAndroid* window_android)
+    : obj_(env, obj),
+      compositor_view_(compositor_view),
+      window_android_(window_android),
+      web_contents_(nullptr) {}
+
+ThinWebView::~ThinWebView() {}
+
+void ThinWebView::Destroy(JNIEnv* env, const JavaParamRef<jobject>& object) {
+  delete this;
+}
+
+void ThinWebView::SetWebContents(JNIEnv* env,
+                                 const JavaParamRef<jobject>& obj,
+                                 const JavaParamRef<jobject>& jweb_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(jweb_contents);
+  SetWebContents(web_contents);
+}
+
+void ThinWebView::SetWebContents(content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  web_contents_ = web_contents;
+  ui::ViewAndroid* view_android = web_contents_->GetNativeView();
+  if (view_android->parent() != window_android_) {
+    window_android_->AddChild(view_android);
+  }
+
+  compositor_view_->SetRootLayer(web_contents_->GetNativeView()->GetLayer());
+  ResizeWebContents(view_size_);
+}
+
+void ThinWebView::SizeChanged(JNIEnv* env,
+                              const JavaParamRef<jobject>& object,
+                              jint width,
+                              jint height) {
+  view_size_ = gfx::Size(width, height);
+
+  // TODO(shaktisahu): If we want to use a different size for WebContents, e.g.
+  // showing full screen contents instead inside this view, don't do the resize.
+  if (web_contents_)
+    ResizeWebContents(view_size_);
+}
+
+void ThinWebView::ResizeWebContents(const gfx::Size& size) {
+  if (!web_contents_)
+    return;
+
+  web_contents_->GetNativeView()->OnPhysicalBackingSizeChanged(size);
+  web_contents_->GetNativeView()->OnSizeChanged(size.width(), size.height());
+}
+
+}  // namespace android
+}  // namespace thin_webview
diff --git a/chrome/browser/android/thin_webview/internal/thin_webview.h b/chrome/browser/android/thin_webview/internal/thin_webview.h
new file mode 100644
index 0000000..6d20792
--- /dev/null
+++ b/chrome/browser/android/thin_webview/internal/thin_webview.h
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_THIN_WEBVIEW_INTERNAL_THIN_WEBVIEW_H_
+#define CHROME_BROWSER_ANDROID_THIN_WEBVIEW_INTERNAL_THIN_WEBVIEW_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "chrome/browser/android/thin_webview/internal/compositor_view_impl.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/android/window_android.h"
+
+namespace thin_webview {
+namespace android {
+
+// Native counterpart of ThinWebViewImpl.java.
+class ThinWebView {
+ public:
+  ThinWebView(JNIEnv* env,
+              jobject obj,
+              CompositorView* compositor_view,
+              ui::WindowAndroid* window_android);
+  ~ThinWebView();
+
+  void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& object);
+
+  void SetWebContents(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& object,
+      const base::android::JavaParamRef<jobject>& jweb_contents);
+
+  void SizeChanged(JNIEnv* env,
+                   const base::android::JavaParamRef<jobject>& object,
+                   jint width,
+                   jint height);
+
+ private:
+  void SetWebContents(content::WebContents* web_contents);
+  void ResizeWebContents(const gfx::Size& size);
+
+  base::android::ScopedJavaGlobalRef<jobject> obj_;
+  CompositorView* compositor_view_;
+  ui::WindowAndroid* window_android_;
+  content::WebContents* web_contents_;
+  gfx::Size view_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThinWebView);
+};
+
+}  // namespace android
+}  // namespace thin_webview
+
+#endif  // CHROME_BROWSER_ANDROID_THIN_WEBVIEW_INTERNAL_THIN_WEBVIEW_H_
diff --git a/chrome/browser/android/thin_webview/java/src/org/chromium/chrome/browser/thinwebview/ThinWebView.java b/chrome/browser/android/thin_webview/java/src/org/chromium/chrome/browser/thinwebview/ThinWebView.java
new file mode 100644
index 0000000..96857d5
--- /dev/null
+++ b/chrome/browser/android/thin_webview/java/src/org/chromium/chrome/browser/thinwebview/ThinWebView.java
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.thinwebview;
+
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import org.chromium.content_public.browser.WebContents;
+
+/**
+ * An android view backed by a {@link Surface} that is able to display a cc::Layer. Either, a {@link
+ * TextureView} or {@link SurfaceView} can be used to provide the surface. The cc::Layer should be
+ * provided in the native.
+ */
+public interface ThinWebView {
+    /**
+     *@return The android {@link View} representing this widget.
+     */
+    View getView();
+
+    /**
+     * Method to be called to display the contents of a {@link WebContents} on the surface. The user
+     * interactability is provided through the {@code contentView}.
+     * @param webContents A {@link WebContents} for providing the contents to be rendered.
+     * @param contentView A {@link ContentView} that can handle user inputs.
+     */
+    void attachWebContents(WebContents webContents, @Nullable View contentView);
+
+    /**
+     * Should be called for cleanup when the CompositorView instance is no longer used.
+     */
+    void destroy();
+}
diff --git a/chrome/browser/android/thin_webview/java/src/org/chromium/chrome/browser/thinwebview/ThinWebViewFactory.java b/chrome/browser/android/thin_webview/java/src/org/chromium/chrome/browser/thinwebview/ThinWebViewFactory.java
new file mode 100644
index 0000000..fe5f03a4
--- /dev/null
+++ b/chrome/browser/android/thin_webview/java/src/org/chromium/chrome/browser/thinwebview/ThinWebViewFactory.java
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.thinwebview;
+
+import android.content.Context;
+
+import org.chromium.chrome.browser.thinwebview.internal.ThinWebViewImpl;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Factory for creating a {@link ThinWebView}.
+ */
+public class ThinWebViewFactory {
+    /**
+     * Creates a {@link ThinWebView} backed by a {@link Surface}. The surface is provided by
+     * a either a {@link TextureView} or {@link SurfaceView}.
+     * @param context The context to create this view.
+     * @param windowAndroid The associated {@code WindowAndroid} on which the view is to be
+     *         displayed.
+     */
+    public static ThinWebView create(Context context, WindowAndroid windowAndroid) {
+        return new ThinWebViewImpl(context, windowAndroid);
+    }
+}
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
index d2365135..945a700 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
 #include "chrome/browser/apps/intent_helper/page_transition_util.h"
 #include "chrome/browser/extensions/extension_util.h"
@@ -160,6 +161,7 @@
     case apps::mojom::AppType::kBuiltIn:
     case apps::mojom::AppType::kCrostini:
     case apps::mojom::AppType::kExtension:
+    case apps::mojom::AppType::kMacNative:
       NOTREACHED();
   }
   RecordUma(launch_name, app_type, close_reason, Source::kHttpOrHttps,
@@ -293,6 +295,8 @@
       return Platform::ARC;
     case PickerAction::PWA_APP_PRESSED:
       return Platform::PWA;
+    case PickerAction::MAC_NATIVE_APP_PRESSED:
+      return Platform::MAC_NATIVE;
     case PickerAction::ERROR_BEFORE_PICKER:
     case PickerAction::ERROR_AFTER_PICKER:
     case PickerAction::DIALOG_DEACTIVATED:
@@ -344,11 +348,12 @@
 }
 
 // static
-bool AppsNavigationThrottle::ContainsOnlyPwas(
+bool AppsNavigationThrottle::ContainsOnlyPwasAndMacApps(
     const std::vector<apps::IntentPickerAppInfo>& apps) {
   return std::all_of(apps.begin(), apps.end(),
                      [](const apps::IntentPickerAppInfo& app_info) {
-                       return app_info.type == apps::mojom::AppType::kWeb;
+                       return app_info.type == apps::mojom::AppType::kWeb ||
+                              app_info.type == apps::mojom::AppType::kMacNative;
                      });
 }
 
@@ -359,7 +364,9 @@
   // if only PWAs are present.
   // TODO(crbug.com/826982): Provide the "Remember my choice" option when the
   // app registry can support persistence for PWAs.
-  return !ContainsOnlyPwas(apps);
+  // TODO(avi): When Chrome gains a UI for managing the persistence of PWAs,
+  // reuse that UI for managing the persistent behavior of Universal Links.
+  return !ContainsOnlyPwasAndMacApps(apps);
 }
 
 bool AppsNavigationThrottle::ShouldDeferNavigationForArc(
@@ -367,6 +374,11 @@
   return false;
 }
 
+std::vector<IntentPickerAppInfo> AppsNavigationThrottle::AppInfoForUrl(
+    const GURL& url) {
+  return std::vector<IntentPickerAppInfo>{};
+}
+
 void AppsNavigationThrottle::ShowIntentPickerForApps(
     content::WebContents* web_contents,
     IntentPickerAutoDisplayService* ui_auto_display_service,
@@ -446,6 +458,8 @@
                                 : PickerAction::ARC_APP_PRESSED;
         case apps::mojom::AppType::kWeb:
           return PickerAction::PWA_APP_PRESSED;
+        case apps::mojom::AppType::kMacNative:
+          return PickerAction::MAC_NATIVE_APP_PRESSED;
         case apps::mojom::AppType::kBuiltIn:
         case apps::mojom::AppType::kCrostini:
         case apps::mojom::AppType::kExtension:
@@ -506,8 +520,11 @@
   }
 
   // We didn't query ARC, so proceed with the navigation and query if we have an
-  // installed desktop PWA to handle the URL.
-  std::vector<IntentPickerAppInfo> apps = FindPwaForUrl(web_contents, url, {});
+  // installed desktop app to handle the URL.
+  std::vector<IntentPickerAppInfo> apps = AppInfoForUrl(url);
+
+  // Perhaps an installed desktop PWA?
+  apps = FindPwaForUrl(web_contents, url, std::move(apps));
 
   if (!apps.empty())
     ui_displayed_ = true;
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
index 62c1576..2981bdd 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.h
@@ -129,17 +129,19 @@
     // Picker errors occurring before the picker is shown.
     ERROR_BEFORE_PICKER = 10,
     INVALID = 11,
-    kMaxValue = INVALID,
+    MAC_NATIVE_APP_PRESSED = 12,
+    kMaxValue = MAC_NATIVE_APP_PRESSED,
   };
 
   // As for PickerAction, these define the buckets for an UMA histogram, so this
-  // must be treated in an append-only fashion. This helps especify where a
+  // must be treated in an append-only fashion. This helps specify where a
   // navigation will continue.
   enum class Platform : int {
     ARC = 0,
     CHROME = 1,
     PWA = 2,
-    kMaxValue = PWA,
+    MAC_NATIVE = 3,
+    kMaxValue = MAC_NATIVE,
   };
 
   // These enums are used to define the intent picker show state, whether the
@@ -170,19 +172,22 @@
 
   static void CloseOrGoBack(content::WebContents* web_contents);
 
-  static bool ContainsOnlyPwas(
+  static bool ContainsOnlyPwasAndMacApps(
       const std::vector<apps::IntentPickerAppInfo>& apps);
 
   static bool ShouldShowPersistenceOptions(
       std::vector<apps::IntentPickerAppInfo>& apps);
 
-  // Overridden for Chrome OS to allow arc handling.
+  // Overrides for Chrome OS to allow ARC handling.
   virtual void MaybeRemoveComingFromArcFlag(content::WebContents* web_contents,
                                             const GURL& previous_url,
                                             const GURL& current_url) {}
 
   virtual bool ShouldDeferNavigationForArc(content::NavigationHandle* handle);
 
+  // Allows a subclass to synchronously provide app info.
+  virtual std::vector<IntentPickerAppInfo> AppInfoForUrl(const GURL& url);
+
   void ShowIntentPickerForApps(
       content::WebContents* web_contents,
       IntentPickerAutoDisplayService* ui_auto_display_service,
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_types.h b/chrome/browser/apps/intent_helper/apps_navigation_types.h
index 34f0a96..b55179aa 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_types.h
+++ b/chrome/browser/apps/intent_helper/apps_navigation_types.h
@@ -84,8 +84,9 @@
   // The icon to be displayed for this app in the picker.
   gfx::Image icon;
 
-  // The string used to launch this app. Represents an Android package name
-  // when type is ARC.
+  // The string used to launch this app. Represents an Android package name when
+  // |type| is kArc, and when |type| is kMacNative, it is the file path of the
+  // native app to use.
   std::string launch_name;
 
   // The string shown to the user to identify this app in the intent picker.
diff --git a/chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.h b/chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.h
new file mode 100644
index 0000000..e365c1b82
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.h
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_INTENT_HELPER_MAC_APPS_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_APPS_INTENT_HELPER_MAC_APPS_NAVIGATION_THROTTLE_H_
+
+#include "chrome/browser/apps/intent_helper/apps_navigation_throttle.h"
+
+// This file implements support for the macOS feature of Universal Links,
+// introduced in macOS 10.15. See:
+// - https://developer.apple.com/ios/universal-links/
+// - https://developer.apple.com/documentation/safariservices/sfuniversallink
+// - https://crbug.com/981337
+
+namespace apps {
+
+class MacAppsNavigationThrottle : public apps::AppsNavigationThrottle {
+ public:
+  // Possibly creates a navigation throttle that checks if any installed apps
+  // can handle the URL being navigated to. The user is prompted if they wish to
+  // open the app or remain in the browser.
+  static std::unique_ptr<apps::AppsNavigationThrottle> MaybeCreate(
+      content::NavigationHandle* handle);
+
+  // Queries for installed apps which can handle |url|, and displays the intent
+  // picker bubble for |web_contents|.
+  static void ShowIntentPickerBubble(
+      content::WebContents* web_contents,
+      IntentPickerAutoDisplayService* ui_auto_display_service,
+      const GURL& url);
+
+  explicit MacAppsNavigationThrottle(
+      content::NavigationHandle* navigation_handle);
+  ~MacAppsNavigationThrottle() override;
+
+ private:
+  // Called when the intent picker is closed for |url|, in |web_contents|, with
+  // |launch_name| as the (possibly empty) action to be triggered based on
+  // |app_type|. |close_reason| gives the reason for the picker being closed,
+  // and |should_persist| is true if the user indicated they wish to remember
+  // the choice made. |ui_auto_display_service| keeps track of whether or not
+  // the user dismissed the ui without engaging with it.
+  static void OnIntentPickerClosed(
+      content::WebContents* web_contents,
+      IntentPickerAutoDisplayService* ui_auto_display_service,
+      const GURL& url,
+      const std::string& launch_name,
+      apps::mojom::AppType app_type,
+      apps::IntentPickerCloseReason close_reason,
+      bool should_persist);
+
+  PickerShowState GetPickerShowState(
+      const std::vector<apps::IntentPickerAppInfo>& apps_for_picker,
+      content::WebContents* web_contents,
+      const GURL& url) override;
+
+  IntentPickerResponse GetOnPickerClosedCallback(
+      content::WebContents* web_contents,
+      IntentPickerAutoDisplayService* ui_auto_display_service,
+      const GURL& url) override;
+
+  std::vector<IntentPickerAppInfo> AppInfoForUrl(const GURL& url) override;
+
+  DISALLOW_COPY_AND_ASSIGN(MacAppsNavigationThrottle);
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_INTENT_HELPER_MAC_APPS_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.mm b/chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.mm
new file mode 100644
index 0000000..5108066
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.mm
@@ -0,0 +1,162 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.h"
+
+#import <Cocoa/Cocoa.h>
+#include <dlfcn.h>
+
+#include "base/mac/sdk_forward_declarations.h"
+#include "base/strings/sys_string_conversions.h"
+#include "content/public/browser/navigation_handle.h"
+#include "net/base/mac/url_conversions.h"
+
+namespace apps {
+
+namespace {
+
+const char kSafariServicesFrameworkPath[] =
+    "/System/Library/Frameworks/SafariServices.framework/"
+    "Versions/Current/SafariServices";
+
+IntentPickerAppInfo AppInfoForAppUrl(NSURL* app_url) {
+  NSString* app_name = nil;
+  if (![app_url getResourceValue:&app_name
+                          forKey:NSURLLocalizedNameKey
+                           error:nil]) {
+    // This shouldn't happen but just in case.
+    app_name = [app_url lastPathComponent];
+  }
+  NSImage* app_icon = nil;
+  if (![app_url getResourceValue:&app_icon
+                          forKey:NSURLEffectiveIconKey
+                           error:nil]) {
+    // This shouldn't happen but just in case.
+    app_icon = [NSImage imageNamed:NSImageNameApplicationIcon];
+  }
+  app_icon.size = NSMakeSize(16, 16);
+
+  return IntentPickerAppInfo(apps::mojom::AppType::kMacNative,
+                             gfx::Image(app_icon),
+                             base::SysNSStringToUTF8([app_url path]),
+                             base::SysNSStringToUTF8(app_name));
+}
+
+SFUniversalLink* GetUniversalLink(const GURL& url) {
+  static void* safari_services = []() -> void* {
+    if (@available(macOS 10.15, *))
+      return dlopen(kSafariServicesFrameworkPath, RTLD_LAZY);
+    return nullptr;
+  }();
+
+  static const Class SFUniversalLink_class =
+      NSClassFromString(@"SFUniversalLink");
+
+  if (!safari_services || !SFUniversalLink_class)
+    return nil;
+
+  return [[[SFUniversalLink_class alloc]
+      initWithWebpageURL:net::NSURLWithGURL(url)] autorelease];
+}
+
+std::vector<IntentPickerAppInfo> AppInfoForUrlImpl(const GURL& url) {
+  std::vector<IntentPickerAppInfo> apps;
+
+  SFUniversalLink* link = GetUniversalLink(url);
+  if (link)
+    apps.push_back(AppInfoForAppUrl(link.applicationURL));
+
+  return apps;
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<apps::AppsNavigationThrottle>
+MacAppsNavigationThrottle::MaybeCreate(content::NavigationHandle* handle) {
+  if (!handle->IsInMainFrame())
+    return nullptr;
+
+  if (!apps::AppsNavigationThrottle::CanCreate(handle->GetWebContents()))
+    return nullptr;
+
+  return std::make_unique<MacAppsNavigationThrottle>(handle);
+}
+
+// static
+void MacAppsNavigationThrottle::ShowIntentPickerBubble(
+    content::WebContents* web_contents,
+    IntentPickerAutoDisplayService* ui_auto_display_service,
+    const GURL& url) {
+  // First, the Universal Link, if there is one.
+  std::vector<IntentPickerAppInfo> apps = AppInfoForUrlImpl(url);
+
+  // Then, any PWAs.
+  apps = apps::AppsNavigationThrottle::FindPwaForUrl(web_contents, url,
+                                                     std::move(apps));
+
+  bool show_persistence_options = ShouldShowPersistenceOptions(apps);
+  apps::AppsNavigationThrottle::ShowIntentPickerBubbleForApps(
+      web_contents, std::move(apps), show_persistence_options,
+      base::BindOnce(&OnIntentPickerClosed, web_contents,
+                     ui_auto_display_service, url));
+}
+
+MacAppsNavigationThrottle::MacAppsNavigationThrottle(
+    content::NavigationHandle* navigation_handle)
+    : apps::AppsNavigationThrottle(navigation_handle) {}
+
+MacAppsNavigationThrottle::~MacAppsNavigationThrottle() = default;
+
+// static
+void MacAppsNavigationThrottle::OnIntentPickerClosed(
+    content::WebContents* web_contents,
+    IntentPickerAutoDisplayService* ui_auto_display_service,
+    const GURL& url,
+    const std::string& launch_name,
+    apps::mojom::AppType app_type,
+    apps::IntentPickerCloseReason close_reason,
+    bool should_persist) {
+  if (app_type == apps::mojom::AppType::kMacNative) {
+    if (close_reason == apps::IntentPickerCloseReason::OPEN_APP) {
+      [[NSWorkspace sharedWorkspace]
+                      openURLs:@[ net::NSURLWithGURL(url) ]
+          withApplicationAtURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(
+                                                          launch_name)]
+                       options:0
+                 configuration:@{}
+                         error:nil];
+    }
+    apps::AppsNavigationThrottle::RecordUma(launch_name, app_type, close_reason,
+                                            Source::kHttpOrHttps,
+                                            should_persist);
+    return;
+  }
+  apps::AppsNavigationThrottle::OnIntentPickerClosed(
+      web_contents, ui_auto_display_service, url, launch_name, app_type,
+      close_reason, should_persist);
+}
+
+apps::AppsNavigationThrottle::PickerShowState
+MacAppsNavigationThrottle::GetPickerShowState(
+    const std::vector<apps::IntentPickerAppInfo>& apps_for_picker,
+    content::WebContents* web_contents,
+    const GURL& url) {
+  return PickerShowState::kOmnibox;
+}
+
+IntentPickerResponse MacAppsNavigationThrottle::GetOnPickerClosedCallback(
+    content::WebContents* web_contents,
+    IntentPickerAutoDisplayService* ui_auto_display_service,
+    const GURL& url) {
+  return base::BindOnce(&OnIntentPickerClosed, web_contents,
+                        ui_auto_display_service, url);
+}
+
+std::vector<IntentPickerAppInfo> MacAppsNavigationThrottle::AppInfoForUrl(
+    const GURL& url) {
+  return AppInfoForUrlImpl(url);
+}
+
+}  // namespace apps
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 33b016e..805faf52 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -84,8 +84,9 @@
 const char* const kChromeOSSettingsSubPages[] = {
     chrome::kAccessibilitySubPage, chrome::kBluetoothSubPage,
     chrome::kDateTimeSubPage,      chrome::kDisplaySubPage,
-    chrome::kInternetSubPage,      chrome::kPowerSubPage,
-    chrome::kStylusSubPage,
+    chrome::kInternetSubPage,      chrome::kNativePrintingSettingsSubPage,
+    chrome::kPowerSubPage,         chrome::kStylusSubPage,
+    chrome::kWiFiSettingsSubPage,
 };
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/background_sync/background_sync_controller_impl.cc b/chrome/browser/background_sync/background_sync_controller_impl.cc
index 1bf97cd..b6f85ab 100644
--- a/chrome/browser/background_sync/background_sync_controller_impl.cc
+++ b/chrome/browser/background_sync/background_sync_controller_impl.cc
@@ -13,9 +13,13 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/variations/variations_associated_data.h"
+#include "content/public/browser/background_sync_context.h"
 #include "content/public/browser/background_sync_controller.h"
 #include "content/public/browser/background_sync_parameters.h"
 #include "content/public/browser/background_sync_registration.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/storage_partition.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -43,7 +47,8 @@
     "min_periodic_sync_events_interval_sec";
 
 BackgroundSyncControllerImpl::BackgroundSyncControllerImpl(Profile* profile)
-    : profile_(profile),
+    : SiteEngagementObserver(SiteEngagementService::Get(profile)),
+      profile_(profile),
       site_engagement_service_(SiteEngagementService::Get(profile)),
       background_sync_metrics_(
           ukm::UkmBackgroundRecorderFactory::GetForProfile(profile_)) {
@@ -53,6 +58,36 @@
 
 BackgroundSyncControllerImpl::~BackgroundSyncControllerImpl() = default;
 
+void BackgroundSyncControllerImpl::OnEngagementEvent(
+    content::WebContents* web_contents,
+    const GURL& url,
+    double score,
+    SiteEngagementService::EngagementType engagement_type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (score == 0.0)
+    return;
+
+  auto origin = url::Origin::Create(url);
+  auto iter = suspended_periodic_sync_origins_.find(origin);
+  if (iter == suspended_periodic_sync_origins_.end())
+    return;
+
+  suspended_periodic_sync_origins_.erase(iter);
+
+  auto* storage_partition = content::BrowserContext::GetStoragePartitionForSite(
+      profile_, url, /* can_create= */ false);
+  if (!storage_partition)
+    return;
+
+  auto* background_sync_context = storage_partition->GetBackgroundSyncContext();
+  if (!background_sync_context)
+    return;
+
+  background_sync_context->RevivePeriodicBackgroundSyncRegistrations(
+      std::move(origin));
+}
+
 void BackgroundSyncControllerImpl::GetParameterOverrides(
     content::BackgroundSyncParameters* parameters) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/background_sync/background_sync_controller_impl.h b/chrome/browser/background_sync/background_sync_controller_impl.h
index 7871ffd..70a79c0c86 100644
--- a/chrome/browser/background_sync/background_sync_controller_impl.h
+++ b/chrome/browser/background_sync/background_sync_controller_impl.h
@@ -15,6 +15,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/background_sync/background_sync_metrics.h"
+#include "chrome/browser/engagement/site_engagement_observer.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -36,6 +37,7 @@
 class GURL;
 
 class BackgroundSyncControllerImpl : public content::BackgroundSyncController,
+                                     public SiteEngagementObserver,
                                      public KeyedService {
  public:
   static const char kFieldTrialName[];
@@ -98,6 +100,13 @@
   void NoteSuspendedPeriodicSyncOrigins(
       std::set<url::Origin> suspended_origins) override;
 
+  // SiteEngagementObserver overrides.
+  void OnEngagementEvent(
+      content::WebContents* web_contents,
+      const GURL& url,
+      double score,
+      SiteEngagementService::EngagementType engagement_type) override;
+
  private:
   // Gets the site engagement penalty for |url|, which is inversely proportional
   // to the engagement level. The lower the engagement levels with the site,
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 61489a7..2768672 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -386,9 +386,9 @@
 #include "components/services/quarantine/public/cpp/quarantine_features_win.h"
 #include "sandbox/win/src/sandbox_policy.h"
 #elif defined(OS_MACOSX)
+#include "chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.h"
 #include "chrome/browser/chrome_browser_main_mac.h"
 #include "services/audio/public/mojom/constants.mojom.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #elif defined(OS_CHROMEOS)
 #include "ash/public/interfaces/constants.mojom.h"
 #include "chrome/browser/ash_service_registry.h"
@@ -2349,11 +2349,9 @@
     const service_manager::Identity& identity,
     base::CommandLine* command_line) {
 #if defined(OS_MACOSX)
-  // On Mac, the video-capture and audio services require a CFRunLoop, provided
-  // by a UI message loop, to run AVFoundation and CoreAudio code.
-  // See https://crbug.com/834581
-  if (identity.name() == video_capture::mojom::kServiceName ||
-      identity.name() == audio::mojom::kServiceName)
+  // On Mac, the audio service requires a CFRunLoop, provided by a UI message
+  // loop, to run AVFoundation and CoreAudio code. See https://crbug.com/834581.
+  if (identity.name() == audio::mojom::kServiceName)
     command_line->AppendSwitch(switches::kMessageLoopTypeUi);
 #endif
 }
@@ -4289,6 +4287,8 @@
     auto url_to_apps_throttle =
 #if defined(OS_CHROMEOS)
         chromeos::ChromeOsAppsNavigationThrottle::MaybeCreate(handle);
+#elif defined(OS_MACOSX)
+        apps::MacAppsNavigationThrottle::MaybeCreate(handle);
 #else
         apps::AppsNavigationThrottle::MaybeCreate(handle);
 #endif
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 81cafb93..193b0fa 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -36,6 +36,7 @@
     "//chromeos/dbus/power:power_manager_proto",
     "//chromeos/strings",
     "//components/policy/proto",
+    "//components/signin/core/browser",
     "//content/app/resources",
     "//ui/accessibility/mojom",
     "//ui/chromeos/resources",
@@ -190,7 +191,6 @@
     "//components/safe_browsing:csd_proto",
     "//components/safe_browsing/db:metadata_proto",
     "//components/session_manager/core",
-    "//components/signin/core/browser",
     "//components/signin/public/identity_manager",
     "//components/signin/public/webdata",
     "//components/storage_monitor",
diff --git a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
index b231235..fd2b1c78d 100644
--- a/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
+++ b/chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.cc
@@ -93,6 +93,7 @@
     case apps::mojom::AppType::kBuiltIn:
     case apps::mojom::AppType::kCrostini:
     case apps::mojom::AppType::kExtension:
+    case apps::mojom::AppType::kMacNative:
       break;
   }
   apps::AppsNavigationThrottle::OnIntentPickerClosed(
@@ -162,6 +163,7 @@
     case PickerAction::CHROME_PREFERRED_PRESSED:
     case PickerAction::OBSOLETE_ALWAYS_PRESSED:
     case PickerAction::OBSOLETE_JUST_ONCE_PRESSED:
+    case PickerAction::MAC_NATIVE_APP_PRESSED:
     case PickerAction::INVALID:
       break;
   }
@@ -286,7 +288,7 @@
   // until "Remember my choice" is available for desktop PWAs.
   // TODO(crbug.com/826982): show the intent picker when the app registry is
   // available to persist "Remember my choice" for PWAs.
-  if (ContainsOnlyPwas(apps_for_picker))
+  if (ContainsOnlyPwasAndMacApps(apps_for_picker))
     return false;
 
   DCHECK(ui_auto_display_service_);
diff --git a/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc b/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
index 51a3fb4..aad63d6 100644
--- a/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
@@ -107,9 +107,13 @@
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender) {
   static const base::Feature constexpr* kFeatureLookup[] = {
-      &features::kUsbbouncer, &features::kUsbguard,
-      &arc::kNativeBridgeExperimentFeature, &arc::kFilePickerExperimentFeature,
-      &arc::kCustomTabsExperimentFeature, &arc::kPrintSpoolerExperimentFeature,
+      &features::kUsbbouncer,
+      &features::kUsbguard,
+      &arc::kBootCompletedBroadcastFeature,
+      &arc::kCustomTabsExperimentFeature,
+      &arc::kFilePickerExperimentFeature,
+      &arc::kNativeBridgeExperimentFeature,
+      &arc::kPrintSpoolerExperimentFeature,
   };
 
   dbus::MessageReader reader(method_call);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 52cb53d6..22b040a 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -94,7 +94,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/signin_error_controller_factory.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
@@ -140,7 +139,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/quirks/quirks_manager.h"
 #include "components/session_manager/core/session_manager.h"
-#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/signin/public/identity_manager/accounts_mutator.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager.cc b/chrome/browser/chromeos/power/ml/user_activity_manager.cc
index a752563..c65e4a7 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager.cc
+++ b/chrome/browser/chromeos/power/ml/user_activity_manager.cc
@@ -306,6 +306,11 @@
   std::move(callback).Run(dim_deferred_);
 }
 
+void UserActivityManager::OnSessionManagerDestroyed() {
+  session_manager_observer_.RemoveAll();
+  session_manager_ = nullptr;
+}
+
 void UserActivityManager::OnSessionStateChanged() {
   DCHECK(session_manager_);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager.h b/chrome/browser/chromeos/power/ml/user_activity_manager.h
index d0f2a9b..80394c74 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager.h
+++ b/chrome/browser/chromeos/power/ml/user_activity_manager.h
@@ -117,6 +117,7 @@
                               UserActivityEvent::ModelPrediction prediction);
 
   // session_manager::SessionManagerObserver overrides:
+  void OnSessionManagerDestroyed() override;
   void OnSessionStateChanged() override;
 
  private:
@@ -202,7 +203,7 @@
                  session_manager::SessionManagerObserver>
       session_manager_observer_;
 
-  session_manager::SessionManager* const session_manager_;
+  session_manager::SessionManager* session_manager_ = nullptr;
 
   mojo::Binding<viz::mojom::VideoDetectorObserver> binding_;
 
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index dd2744f5..e6e00380 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -38,9 +38,9 @@
      "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"},
     {"cros-termina", "770.1",
      "e9d960f84f628e1f42d05de4046bb5b3154b6f1f65c08412c6af57a29aecaffb"},
-    {"rtanalytics-light", "12.0",
+    {"rtanalytics-light", "13.0",
      "69f09d33c439c2ab55bbbe24b47ab55cb3f6c0bd1f1ef46eefea3216ec925038"},
-    {"rtanalytics-full", "12.0",
+    {"rtanalytics-full", "13.0",
      "c93c3e1013c52100a20038b405ac854d69fa889f6dc4fa6f188267051e05e444"},
     {"star-cups-driver", "1.1",
      "6d24de30f671da5aee6d463d9e446cafe9ddac672800a9defe86877dcde6c466"},
diff --git a/chrome/browser/enterprise_reporting/report_generator_unittest.cc b/chrome/browser/enterprise_reporting/report_generator_unittest.cc
index f9469dc..8fbf7a2 100644
--- a/chrome/browser/enterprise_reporting/report_generator_unittest.cc
+++ b/chrome/browser/enterprise_reporting/report_generator_unittest.cc
@@ -39,6 +39,16 @@
 #endif
 }
 
+// Controls the way of Profile creation which affects report.
+enum ProfileStatus {
+  // Idle Profile does not generate full report.
+  kIdle,
+  // Active Profile generates full report.
+  kActive,
+  // Active Profile generate large full report.
+  kActiveWithContent,
+};
+
 // Verify the name is in the set. Remove the name from the set afterwards.
 void FindAndRemoveProfileName(std::set<std::string>* names,
                               const std::string& name) {
@@ -47,6 +57,18 @@
   names->erase(it);
 }
 
+void AddExtensionToProfile(TestingProfile* profile) {
+  extensions::ExtensionRegistry* extension_registry =
+      extensions::ExtensionRegistry::Get(profile);
+
+  std::string extension_name =
+      "a super super super super super super super super super super super "
+      "super super super super super super long extension name";
+  extension_registry->AddEnabled(extensions::ExtensionBuilder(extension_name)
+                                     .SetID("abcdefghijklmnoabcdefghijklmnoab")
+                                     .Build());
+}
+
 #endif
 }  // namespace
 
@@ -71,19 +93,28 @@
   // |is_active| is true. Otherwise, information is only put into
   // ProfileAttributesStorage.
   std::set<std::string> CreateProfiles(int number,
-                                       bool is_active,
-                                       int start_index = 0) {
+                                       ProfileStatus status,
+                                       int start_index = 0,
+                                       bool with_extension = false) {
     std::set<std::string> profile_names;
     for (int i = start_index; i < number; i++) {
       std::string profile_name =
           std::string(kProfile) + base::NumberToString(i);
-      if (is_active) {
-        profile_manager_.CreateTestingProfile(profile_name);
-      } else {
-        profile_manager_.profile_attributes_storage()->AddProfile(
-            profile_manager()->profiles_dir().AppendASCII(profile_name),
-            base::ASCIIToUTF16(profile_name), std::string(), base::string16(),
-            0, std::string(), EmptyAccountId());
+      switch (status) {
+        case kIdle:
+          profile_manager_.profile_attributes_storage()->AddProfile(
+              profile_manager()->profiles_dir().AppendASCII(profile_name),
+              base::ASCIIToUTF16(profile_name), std::string(), base::string16(),
+              0, std::string(), EmptyAccountId());
+          break;
+        case kActive:
+          profile_manager_.CreateTestingProfile(profile_name);
+          break;
+        case kActiveWithContent:
+          TestingProfile* profile =
+              profile_manager_.CreateTestingProfile(profile_name);
+          AddExtensionToProfile(profile);
+          break;
       }
       profile_names.insert(profile_name);
     }
@@ -152,7 +183,7 @@
 };
 
 TEST_F(ReportGeneratorTest, GenerateBasicReport) {
-  auto profile_names = CreateProfiles(/*number*/ 2, /*is_active=*/false);
+  auto profile_names = CreateProfiles(/*number*/ 2, kIdle);
 
   auto requests = GenerateRequests();
   EXPECT_EQ(1u, requests.size());
@@ -179,10 +210,9 @@
 }
 
 TEST_F(ReportGeneratorTest, GenerateActiveProfiles) {
-  auto inactive_profiles_names =
-      CreateProfiles(/*number*/ 2, /*is_active=*/false);
+  auto inactive_profiles_names = CreateProfiles(/*number*/ 2, kIdle);
   auto active_profiles_names =
-      CreateProfiles(/*number*/ 2, /*is_active=*/true, /*start_index*/ 2);
+      CreateProfiles(/*number*/ 2, kActive, /*start_index*/ 2);
 
   auto requests = GenerateRequests();
   EXPECT_EQ(1u, requests.size());
@@ -192,7 +222,7 @@
 }
 
 TEST_F(ReportGeneratorTest, BasicReportIsTooBig) {
-  CreateProfiles(/*number*/ 2, /*is_active=*/false);
+  CreateProfiles(/*number*/ 2, kIdle);
 
   // Set a super small limitation.
   generator()->SetMaximumReportSizeForTesting(5);
@@ -202,8 +232,9 @@
   EXPECT_EQ(0u, requests.size());
 }
 
-TEST_F(ReportGeneratorTest, DISABLED_ReportSeparation) {
-  auto profile_names = CreateProfiles(/*number*/ 2, /*is_active=*/true);
+TEST_F(ReportGeneratorTest, ReportSeparation) {
+  auto profile_names =
+      CreateProfiles(/*number*/ 2, kActiveWithContent, /*start_index*/ 0);
 
   // Set the limitation just below the size of the report so that it needs to be
   // separated into two requests later.
@@ -229,20 +260,8 @@
 }
 
 TEST_F(ReportGeneratorTest, ProfileReportIsTooBig) {
-  TestingProfile* first_profile =
-      profile_manager()->CreateTestingProfile(kProfile);
-  std::set<std::string> first_profile_name = {kProfile};
-
-  // Add more things into the Profile to make the report bigger.
-  extensions::ExtensionRegistry* extension_registry =
-      extensions::ExtensionRegistry::Get(first_profile);
-
-  std::string extension_name =
-      "a super super super super super super super super super super super "
-      "super super super super super super long extension name";
-  extension_registry->AddEnabled(extensions::ExtensionBuilder(extension_name)
-                                     .SetID("abcdefghijklmnoabcdefghijklmnoab")
-                                     .Build());
+  std::set<std::string> first_profile_name =
+      CreateProfiles(/*number*/ 1, kActiveWithContent, /*start_index*/ 0);
 
   // Set the limitation just below the size of the report.
   auto requests = GenerateRequests();
@@ -250,7 +269,8 @@
   generator()->SetMaximumReportSizeForTesting(requests[0]->ByteSizeLong() - 30);
 
   // Add a smaller Profile.
-  auto second_profile_name = CreateProfiles(/*number*/ 1, /*is_active=*/true);
+  auto second_profile_name =
+      CreateProfiles(/*number*/ 1, kActive, /*start_index*/ 1);
 
   requests = GenerateRequests();
 
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index baecbf5..0a86a59 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -765,6 +765,7 @@
     "//chrome/common/extensions/api",
     "//components/safe_browsing:csd_proto",
     "//components/safe_browsing/db:util",
+    "//components/signin/core/browser",
     "//content/public/browser",
   ]
   deps = [
@@ -835,7 +836,6 @@
     "//components/search_engines",
     "//components/services/unzip/public/cpp",
     "//components/sessions",
-    "//components/signin/core/browser",
     "//components/signin/public/identity_manager",
     "//components/spellcheck/browser",
     "//components/storage_monitor",
diff --git a/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc b/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
index ef5dbf77c..312afc2b 100644
--- a/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_api_non_persistent_apitest.cc
@@ -49,10 +49,12 @@
   }
 };
 
-// Test has been flaky on all platforms. https://crbug.com/985936.
 // Tests chrome.management.uninstallSelf API.
-IN_PROC_BROWSER_TEST_P(ManagementApiNonPersistentApiTest,
-                       DISABLED_UninstallSelf) {
+IN_PROC_BROWSER_TEST_P(ManagementApiNonPersistentApiTest, UninstallSelf) {
+  if (GetParam() == ContextType::kEventPage) {
+    // Test has been flaky on all platforms. https://crbug.com/985936.
+    return;
+  }
   constexpr char kEventPageBackgroundScript[] = R"({"scripts": ["script.js"]})";
   constexpr char kServiceWorkerBackgroundScript[] =
       R"({"service_worker": "script.js"})";
diff --git a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
index 633d8ff..9d49d65 100644
--- a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
+++ b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
@@ -13,12 +13,9 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h"
 #include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/system_connector.h"
+#include "content/public/browser/video_capture_service.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
 
 namespace extensions {
 
@@ -68,22 +65,18 @@
       base::BindOnce(OnLoadComponent, std::move(load_callback)));
 }
 
-void MediaPerceptionAPIDelegateChromeOS::
-    BindDeviceFactoryProviderToVideoCaptureService(
-        video_capture::mojom::DeviceFactoryProviderPtr* provider) {
+void MediaPerceptionAPIDelegateChromeOS::BindVideoSourceProvider(
+    mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  // In unit test environments, there may not be any connector.
-  service_manager::Connector* connector = content::GetSystemConnector();
-  if (!connector)
-    return;
-  connector->BindInterface(video_capture::mojom::kServiceName, provider);
-
   video_capture::mojom::AcceleratorFactoryPtr accelerator_factory;
   mojo::MakeStrongBinding(
       std::make_unique<
           content::DelegateToBrowserGpuServiceAcceleratorFactory>(),
       mojo::MakeRequest(&accelerator_factory));
-  (*provider)->InjectGpuDependencies(std::move(accelerator_factory));
+
+  auto& service = content::GetVideoCaptureService();
+  service.InjectGpuDependencies(std::move(accelerator_factory));
+  service.ConnectToVideoSourceProvider(std::move(receiver));
 }
 
 void MediaPerceptionAPIDelegateChromeOS::SetMediaPerceptionRequestHandler(
diff --git a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h
index 1d765110..45abc51 100644
--- a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h
+++ b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h
@@ -20,8 +20,9 @@
   void LoadCrOSComponent(
       const api::media_perception_private::ComponentType& type,
       LoadCrOSComponentCallback load_callback) override;
-  void BindDeviceFactoryProviderToVideoCaptureService(
-      video_capture::mojom::DeviceFactoryProviderPtr* provider) override;
+  void BindVideoSourceProvider(
+      mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver)
+      override;
   void SetMediaPerceptionRequestHandler(
       MediaPerceptionRequestHandler handler) override;
   void ForwardMediaPerceptionRequest(
diff --git a/chrome/browser/extensions/chrome_extensions_interface_registration.cc b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
index 1483684..d7b9be5 100644
--- a/chrome/browser/extensions/chrome_extensions_interface_registration.cc
+++ b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
@@ -16,6 +16,7 @@
 #include "chrome/common/extensions/extension_constants.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/video_capture_service.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/permissions/api_permission.h"
 #include "extensions/common/permissions/permissions_data.h"
@@ -28,15 +29,12 @@
 #include "chromeos/services/media_perception/public/mojom/media_perception.mojom.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/media_device_id.h"
-#include "content/public/browser/system_connector.h"
 #include "extensions/browser/api/extensions_api_client.h"
 #include "extensions/browser/api/media_perception_private/media_perception_api_delegate.h"
 #include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
 #include "media/capture/video/chromeos/renderer_facing_cros_image_capture.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #endif
@@ -83,9 +81,8 @@
   cros::mojom::CrosImageCapturePtr proxy_ptr;
   auto proxy_request = mojo::MakeRequest(&proxy_ptr);
 
-  // Bind proxy request to video_capture service.
-  content::GetSystemConnector()->BindInterface(
-      video_capture::mojom::kServiceName, std::move(proxy_request));
+  // Bind the interface through the Video Capture service.
+  content::GetVideoCaptureService().BindCrosImageCapture(std::move(request));
 
   auto security_origin = source->GetLastCommittedOrigin();
   auto media_device_id_salt =
diff --git a/chrome/browser/extensions/web_contents_browsertest.cc b/chrome/browser/extensions/web_contents_browsertest.cc
index ba87152..3f7dced 100644
--- a/chrome/browser/extensions/web_contents_browsertest.cc
+++ b/chrome/browser/extensions/web_contents_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/test/browser_test_utils.h"
@@ -148,7 +149,8 @@
 
   // Returns the cached frame data for |rfh|.
   auto get_frame_data = [](content::RenderFrameHost* rfh) {
-    return ExtensionApiFrameIdMap::Get()->GetFrameData(rfh);
+    return ExtensionApiFrameIdMap::Get()->GetFrameData(
+        rfh->GetProcess()->GetID(), rfh->GetRoutingID());
   };
 
   // Adds an iframe with the given |name| and |src| to the given |web_contents|
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 68123f2..87782698 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -31,6 +31,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/metrics_services_manager/metrics_services_manager.h"
+#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/sync/driver/profile_sync_service.h"
 #include "components/sync/driver/sync_driver_switches.h"
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index e73a525fb..27ebbc3 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -55,6 +55,7 @@
 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #endif
@@ -101,6 +102,10 @@
 }
 
 bool ShouldUseBuiltinCertVerifier(Profile* profile) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(chromeos::switches::kForceCertVerifierBuiltin))
+    return true;
+
   if (chromeos::ProfileHelper::Get()->IsSigninProfile(profile) ||
       chromeos::ProfileHelper::Get()->IsLockScreenAppProfile(profile)) {
     // No need to override the feature-set setting through policy for sign-in
@@ -181,8 +186,8 @@
 
 #if defined(OS_CHROMEOS)
   using_builtin_cert_verifier_ = ShouldUseBuiltinCertVerifier(profile_);
-  DVLOG(1) << "Using " << (using_builtin_cert_verifier_ ? "built-in" : "legacy")
-           << " cert verifier.";
+  VLOG(0) << "Using " << (using_builtin_cert_verifier_ ? "built-in" : "legacy")
+          << " cert verifier.";
 #endif
   // When any of the following CT preferences change, we schedule an update
   // to aggregate the actual update using a |ct_policy_update_timer_|.
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index 156b0226..e98c831 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -84,11 +84,15 @@
 
 void GetStubResolverConfig(
     PrefService* local_state,
-    bool* stub_resolver_enabled,
+    bool* insecure_stub_resolver_enabled,
+    net::DnsConfig::SecureDnsMode* secure_dns_mode,
     base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>*
         dns_over_https_servers) {
   DCHECK(!dns_over_https_servers->has_value());
 
+  *insecure_stub_resolver_enabled =
+      local_state->GetBoolean(prefs::kBuiltInDnsClientEnabled);
+
   const auto& doh_server_list =
       local_state->GetList(prefs::kDnsOverHttpsServers)->GetList();
   const auto& doh_server_method_list =
@@ -128,20 +132,24 @@
     }
   }
 
-  *stub_resolver_enabled =
-      dns_over_https_servers->has_value() ||
-      local_state->GetBoolean(prefs::kBuiltInDnsClientEnabled);
+  // TODO(crbug.com/985589): Read secure dns mode from prefs.
+  if (dns_over_https_servers->has_value())
+    *secure_dns_mode = net::DnsConfig::SecureDnsMode::AUTOMATIC;
+  else
+    *secure_dns_mode = net::DnsConfig::SecureDnsMode::OFF;
 }
 
 void OnStubResolverConfigChanged(PrefService* local_state,
                                  const std::string& pref_name) {
-  bool stub_resolver_enabled;
+  bool insecure_stub_resolver_enabled;
+  net::DnsConfig::SecureDnsMode secure_dns_mode;
   base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>
       dns_over_https_servers;
-  GetStubResolverConfig(local_state, &stub_resolver_enabled,
-                        &dns_over_https_servers);
+  GetStubResolverConfig(local_state, &insecure_stub_resolver_enabled,
+                        &secure_dns_mode, &dns_over_https_servers);
   content::GetNetworkService()->ConfigureStubHostResolver(
-      stub_resolver_enabled, std::move(dns_over_https_servers));
+      insecure_stub_resolver_enabled, secure_dns_mode,
+      std::move(dns_over_https_servers));
 }
 
 // Constructs HttpAuthStaticParams based on |local_state|.
@@ -554,13 +562,15 @@
 
   // Configure the stub resolver. This must be done after the system
   // NetworkContext is created, but before anything has the chance to use it.
-  bool stub_resolver_enabled;
+  bool insecure_stub_resolver_enabled;
+  net::DnsConfig::SecureDnsMode secure_dns_mode;
   base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>
       dns_over_https_servers;
-  GetStubResolverConfig(local_state_, &stub_resolver_enabled,
-                        &dns_over_https_servers);
+  GetStubResolverConfig(local_state_, &insecure_stub_resolver_enabled,
+                        &secure_dns_mode, &dns_over_https_servers);
   content::GetNetworkService()->ConfigureStubHostResolver(
-      stub_resolver_enabled, std::move(dns_over_https_servers));
+      insecure_stub_resolver_enabled, secure_dns_mode,
+      std::move(dns_over_https_servers));
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   const base::CommandLine& command_line =
@@ -706,10 +716,12 @@
 }
 
 void SystemNetworkContextManager::GetStubResolverConfigForTesting(
-    bool* stub_resolver_enabled,
+    bool* insecure_stub_resolver_enabled,
+    net::DnsConfig::SecureDnsMode* secure_dns_mode,
     base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>*
         dns_over_https_servers) {
-  GetStubResolverConfig(g_browser_process->local_state(), stub_resolver_enabled,
+  GetStubResolverConfig(g_browser_process->local_state(),
+                        insecure_stub_resolver_enabled, secure_dns_mode,
                         dns_over_https_servers);
 }
 
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index 59c34253..87c657e2 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -130,7 +130,8 @@
 
   // Returns configuration that would be sent to the stub DNS resolver.
   static void GetStubResolverConfigForTesting(
-      bool* stub_resolver_enabled,
+      bool* insecure_stub_resolver_enabled,
+      net::DnsConfig::SecureDnsMode* secure_dns_mode,
       base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>*
           dns_over_https_servers);
 
diff --git a/chrome/browser/net/system_network_context_manager_browsertest.cc b/chrome/browser/net/system_network_context_manager_browsertest.cc
index 8010454..48323a3 100644
--- a/chrome/browser/net/system_network_context_manager_browsertest.cc
+++ b/chrome/browser/net/system_network_context_manager_browsertest.cc
@@ -30,25 +30,31 @@
 namespace {
 
 void GetStubResolverConfig(
-    bool* stub_resolver_enabled,
+    bool* insecure_stub_resolver_enabled,
+    net::DnsConfig::SecureDnsMode* secure_dns_mode,
     base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>*
         dns_over_https_servers) {
   dns_over_https_servers->reset();
 
   SystemNetworkContextManager::GetStubResolverConfigForTesting(
-      stub_resolver_enabled, dns_over_https_servers);
+      insecure_stub_resolver_enabled, secure_dns_mode, dns_over_https_servers);
 }
 
 // Checks the values returned by GetStubResolverConfigForTesting() match
 // |async_dns_feature_enabled| (With empty DNS over HTTPS prefs). Then sets
 // various DNS over HTTPS servers, and makes sure the settings are respected.
+// TODO(crbug.com/985589): Check that the SecureDnsMode is read correctly from
+// the prefs once it is stored there.
 void RunStubResolverConfigTests(bool async_dns_feature_enabled) {
   // Check initial state.
-  bool stub_resolver_enabled = !async_dns_feature_enabled;
+  bool insecure_stub_resolver_enabled = !async_dns_feature_enabled;
+  net::DnsConfig::SecureDnsMode secure_dns_mode;
   base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>
       dns_over_https_servers;
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
 
   // Check state after setting various DNS over HTTPS preferences.
@@ -74,8 +80,10 @@
   servers.GetList().push_back(base::Value(kGoodGetTemplate));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
   servers.GetList().clear();
   methods.GetList().clear();
@@ -83,8 +91,10 @@
   methods.GetList().push_back(base::Value(kPost));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
   servers.GetList().clear();
   methods.GetList().clear();
@@ -94,8 +104,10 @@
   methods.GetList().push_back(base::Value(kPost));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
   servers.GetList().clear();
   methods.GetList().clear();
@@ -105,8 +117,10 @@
   methods.GetList().push_back(base::Value(3.14));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
   servers.GetList().clear();
   methods.GetList().clear();
@@ -116,8 +130,10 @@
   methods.GetList().push_back(base::Value(kPost));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
   servers.GetList().clear();
   methods.GetList().clear();
@@ -127,8 +143,10 @@
   methods.GetList().push_back(base::Value(kPost));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(true, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::AUTOMATIC, secure_dns_mode);
   ASSERT_TRUE(dns_over_https_servers.has_value());
   ASSERT_EQ(1u, dns_over_https_servers->size());
   EXPECT_EQ(kGoodPostTemplate, dns_over_https_servers->at(0)->server_template);
@@ -143,8 +161,10 @@
   methods.GetList().push_back(base::Value(kPost));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(true, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::AUTOMATIC, secure_dns_mode);
   ASSERT_TRUE(dns_over_https_servers.has_value());
   ASSERT_EQ(1u, dns_over_https_servers->size());
   EXPECT_EQ(kGoodGetTemplate, dns_over_https_servers->at(0)->server_template);
@@ -159,8 +179,10 @@
   methods.GetList().push_back(base::Value(kGet));
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(true, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::AUTOMATIC, secure_dns_mode);
   ASSERT_TRUE(dns_over_https_servers.has_value());
   ASSERT_EQ(2u, dns_over_https_servers->size());
   EXPECT_EQ(kGoodPostTemplate, dns_over_https_servers->at(0)->server_template);
@@ -173,13 +195,17 @@
   // Test case with policy BuiltInDnsClientEnabled enabled.
   local_state->Set(prefs::kDnsOverHttpsServers, servers);
   local_state->Set(prefs::kDnsOverHttpsServerMethods, methods);
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
   local_state->Set(prefs::kBuiltInDnsClientEnabled,
                    base::Value(!async_dns_feature_enabled));
-  GetStubResolverConfig(&stub_resolver_enabled, &dns_over_https_servers);
-  EXPECT_EQ(!async_dns_feature_enabled, stub_resolver_enabled);
+  GetStubResolverConfig(&insecure_stub_resolver_enabled, &secure_dns_mode,
+                        &dns_over_https_servers);
+  EXPECT_EQ(!async_dns_feature_enabled, insecure_stub_resolver_enabled);
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, secure_dns_mode);
   EXPECT_FALSE(dns_over_https_servers.has_value());
 }
 
diff --git a/chrome/browser/notifications/scheduler/internal/BUILD.gn b/chrome/browser/notifications/scheduler/internal/BUILD.gn
index d956686..e27fae02 100644
--- a/chrome/browser/notifications/scheduler/internal/BUILD.gn
+++ b/chrome/browser/notifications/scheduler/internal/BUILD.gn
@@ -69,6 +69,7 @@
     "//components/keyed_service/core",
     "//components/leveldb_proto",
     "//skia",
+    "//ui/gfx/codec",
   ]
 }
 
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
index 5e5e3d7..dd67b4e 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
@@ -7,13 +7,18 @@
 #include <algorithm>
 #include <map>
 #include <unordered_set>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/guid.h"
 #include "base/memory/weak_ptr.h"
+#include "base/task/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/notifications/scheduler/internal/icon_store.h"
 #include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
 #include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h"
 #include "chrome/browser/notifications/scheduler/public/notification_params.h"
 
 namespace notifications {
@@ -75,14 +80,16 @@
     notification_store_->Add(
         guid, *entry_ptr,
         base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationAdded,
-                       weak_ptr_factory_.GetWeakPtr()));
+                       weak_ptr_factory_.GetWeakPtr(), type, guid));
   }
 
   void DisplayNotification(const std::string& guid) override {
     std::unique_ptr<NotificationEntry> entry;
     for (auto it = notifications_.begin(); it != notifications_.end(); it++) {
-      if (it->second.count(guid))
+      if (it->second.count(guid)) {
         entry = std::move(it->second[guid]);
+        break;
+      }
     }
 
     DCHECK(entry);
@@ -94,7 +101,7 @@
     notification_store_->Delete(
         guid,
         base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted,
-                       weak_ptr_factory_.GetWeakPtr()));
+                       weak_ptr_factory_.GetWeakPtr(), entry->icons_uuid));
 
     if (delegate_)
       delegate_->DisplayNotification(std::move(entry));
@@ -140,7 +147,7 @@
           entry.guid,
           base::BindOnce(
               &ScheduledNotificationManagerImpl::OnNotificationDeleted,
-              weak_ptr_factory_.GetWeakPtr()));
+              weak_ptr_factory_.GetWeakPtr(), entry.icons_uuid));
     }
     notifications_.erase(type);
   }
@@ -177,7 +184,7 @@
             entry->guid,
             base::BindOnce(
                 &ScheduledNotificationManagerImpl::OnNotificationDeleted,
-                weak_ptr_factory_.GetWeakPtr()));
+                weak_ptr_factory_.GetWeakPtr(), entry->icons_uuid));
       } else if (clients_.count(entry->type)) {
         notifications_[entry->type].emplace(entry->guid, std::move(*it));
       }
@@ -193,14 +200,75 @@
     std::move(callback).Run(success);
   }
 
-  void OnNotificationAdded(bool success) { NOTIMPLEMENTED(); }
+  void OnNotificationAdded(SchedulerClientType type,
+                           std::string guid,
+                           bool success) {
+    auto* entry = FindNotificationEntry(type, guid);
+    if (!entry)
+      return;
+    // TODO(hesen): More Error handling.
+    if (!success) {
+      notifications_[type].erase(guid);
+      if (notifications_[type].empty())
+        notifications_.erase(type);
+      return;
+    }
 
-  void OnNotificationDeleted(bool success) { NOTIMPLEMENTED(); }
+    // TODO(hesen): Address only first icon for now. Handle all icons situation
+    // in following CLs.
+    notifications::ConvertIconToString(
+        std::move(entry->notification_data.icons.front()),
+        base::BindOnce(&ScheduledNotificationManagerImpl::OnIconEncoded,
+                       weak_ptr_factory_.GetWeakPtr(), type, std::move(guid)));
+  }
 
-  void OnIconAdded(bool success) { NOTIMPLEMENTED(); }
+  void OnIconEncoded(SchedulerClientType type,
+                     std::string guid,
+                     std::string encoded_data) {
+    auto icon_entry = std::make_unique<IconEntry>();
+    auto icon_uuid = base::GenerateGUID();
+    icon_entry->uuid = icon_uuid;
+    icon_entry->data = std::move(encoded_data);
+    icon_store_->Add(
+        icon_uuid, std::move(icon_entry),
+        base::BindOnce(&ScheduledNotificationManagerImpl::OnIconAdded,
+                       weak_ptr_factory_.GetWeakPtr(), type, std::move(guid),
+                       std::move(icon_uuid)));
+  }
+
+  void OnIconAdded(SchedulerClientType type,
+                   std::string guid,
+                   std::string icon_uuid,
+                   bool success) {
+    auto* entry = FindNotificationEntry(type, std::move(guid));
+    // TODO(hesen): Error handling.
+    if (!entry || !success)
+      return;
+    entry->icons_uuid.emplace_back(std::move(icon_uuid));
+  }
+
+  void OnNotificationDeleted(const std::vector<std::string>& icon_uuids,
+                             bool success) {
+    // TODO(hesen): Error handling.
+    if (!success)
+      return;
+    for (const auto& icon_uuid : icon_uuids) {
+      icon_store_->Delete(
+          std::move(icon_uuid),
+          base::BindOnce(&ScheduledNotificationManagerImpl::OnIconDeleted,
+                         weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
 
   void OnIconDeleted(bool success) { NOTIMPLEMENTED(); }
 
+  NotificationEntry* FindNotificationEntry(SchedulerClientType type,
+                                           const std::string& guid) {
+    if (!notifications_.count(type) || !notifications_[type].count(guid))
+      return nullptr;
+    return notifications_[type][guid].get();
+  }
+
   NotificationStore notification_store_;
   std::unique_ptr<IconStore> icon_store_;
   const std::unordered_set<SchedulerClientType> clients_;
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
index 2731948..578f2d6 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
@@ -5,9 +5,14 @@
 #include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h"
 
 #include <algorithm>
+#include <utility>
+#include <vector>
 
+#include "base/task/post_task.h"
+#include "base/threading/thread_restrictions.h"
 #include "chrome/browser/notifications/scheduler/internal/impression_types.h"
 #include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
+#include "ui/gfx/codec/png_codec.h"
 
 namespace notifications {
 namespace {
@@ -35,6 +40,23 @@
                        });
   return base::make_optional<FirstAndLastIters>(first, last - 1);
 }
+
+// Converts SkBitmap to String.
+std::string ConvertIconToStringOnIOThread(SkBitmap image) {
+  base::AssertLongCPUWorkAllowed();
+  std::vector<unsigned char> image_data;
+  gfx::PNGCodec::EncodeBGRASkBitmap(std::move(image), false, &image_data);
+  std::string result(image_data.begin(), image_data.end());
+  return result;
+}
+
+// Converts SkBitmap to String.
+SkBitmap ConvertStringToIconOnIOThread(std::string data) {
+  SkBitmap image;
+  gfx::PNGCodec::Decode(reinterpret_cast<const unsigned char*>(data.data()),
+                        data.length(), &image);
+  return image;
+}
 }  // namespace
 
 bool ToLocalHour(int hour,
@@ -113,4 +135,24 @@
   return client_state;
 }
 
+// Converts SkBitmap to String.
+void ConvertIconToString(SkBitmap image,
+                         base::OnceCallback<void(std::string)> callback) {
+  DCHECK(callback);
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+      base::BindOnce(&ConvertIconToStringOnIOThread, std::move(image)),
+      std::move(callback));
+}
+
+// Converts String to SkBitmap.
+void ConvertStringToIcon(std::string data,
+                         base::OnceCallback<void(SkBitmap)> callback) {
+  DCHECK(callback);
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+      base::BindOnce(&ConvertStringToIconOnIOThread, std::move(data)),
+      std::move(callback));
+}
+
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.h b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
index 28733ac..5389ed5 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
@@ -8,11 +8,13 @@
 #include <deque>
 #include <map>
 #include <memory>
+#include <string>
 
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 
 namespace notifications {
 
@@ -48,6 +50,14 @@
     SchedulerClientType type,
     const SchedulerConfig& config);
 
+// Converts SkBitmap icon to String.
+void ConvertIconToString(SkBitmap image,
+                         base::OnceCallback<void(std::string)> callback);
+
+// Converts String to SkBitmap icon.
+void ConvertStringToIcon(std::string data,
+                         base::OnceCallback<void(SkBitmap)> callback);
+
 }  // namespace notifications
 
 #endif  // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
index 7cee9c41..de5e56c 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -91,15 +91,14 @@
     "PageLoad.Experimental.PaintTiming.NavigationToLargestImagePaint";
 const char kHistogramLargestTextPaint[] =
     "PageLoad.Experimental.PaintTiming.NavigationToLargestTextPaint";
-const char kHistogramLargestContentPaint[] =
-    "PageLoad.Experimental.PaintTiming.NavigationToLargestContentPaint";
-const char kHistogramLargestContentPaintContentType[] =
-    "PageLoad.Experimental.PaintTiming.LargestContentPaint.ContentType";
-const char kHistogramLargestContentPaintAllFrames[] =
-    "PageLoad.Experimental.PaintTiming.NavigationToLargestContentPaint."
-    "AllFrames";
-const char kHistogramLargestContentPaintAllFramesContentType[] =
-    "PageLoad.Experimental.PaintTiming.LargestContentPaint.AllFrames."
+const char kHistogramLargestContentfulPaint[] =
+    "PageLoad.PaintTiming.NavigationToLargestContentfulPaint";
+const char kHistogramLargestContentfulPaintContentType[] =
+    "PageLoad.Internal.PaintTiming.LargestContentfulPaint.ContentType";
+const char kHistogramLargestContentfulPaintMainFrame[] =
+    "PageLoad.PaintTiming.NavigationToLargestContentfulPaint.MainFrame";
+const char kHistogramLargestContentfulPaintMainFrameContentType[] =
+    "PageLoad.Internal.PaintTiming.LargestContentfulPaint.MainFrame."
     "ContentType";
 const char kHistogramTimeToInteractive[] =
     "PageLoad.Experimental.NavigationToInteractive";
@@ -789,7 +788,7 @@
 // collected yet. This is meant to be called at the end of a page lifetime, for
 // example, when the user is navigating away from the page.
 void CorePageLoadMetricsObserver::RecordTimingHistograms(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::mojom::PageLoadTiming& main_frame_timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
   // Log time to first foreground / time to first background. Log counts that we
   // started a relevant page load in the foreground / background.
@@ -799,27 +798,29 @@
   }
 
   if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->largest_image_paint, info)) {
-    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestImagePaint,
-                        timing.paint_timing->largest_image_paint.value());
+          main_frame_timing.paint_timing->largest_image_paint, info)) {
+    PAGE_LOAD_HISTOGRAM(
+        internal::kHistogramLargestImagePaint,
+        main_frame_timing.paint_timing->largest_image_paint.value());
   }
   if (WasStartedInForegroundOptionalEventInForeground(
-          timing.paint_timing->largest_text_paint, info)) {
-    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestTextPaint,
-                        timing.paint_timing->largest_text_paint.value());
+          main_frame_timing.paint_timing->largest_text_paint, info)) {
+    PAGE_LOAD_HISTOGRAM(
+        internal::kHistogramLargestTextPaint,
+        main_frame_timing.paint_timing->largest_text_paint.value());
   }
   base::Optional<base::TimeDelta> largest_content_paint_time;
   uint64_t largest_content_paint_size;
   PageLoadMetricsObserver::LargestContentType largest_content_type;
   if (AssignTimeAndSizeForLargestContentfulPaint(
-          timing.paint_timing, &largest_content_paint_time,
+          main_frame_timing.paint_timing, &largest_content_paint_time,
           &largest_content_paint_size, &largest_content_type) &&
       WasStartedInForegroundOptionalEventInForeground(
           largest_content_paint_time, info)) {
-    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentPaint,
+    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaintMainFrame,
                         largest_content_paint_time.value());
     UMA_HISTOGRAM_ENUMERATION(
-        internal::kHistogramLargestContentPaintContentType,
+        internal::kHistogramLargestContentfulPaintMainFrameContentType,
         largest_content_type);
   }
 
@@ -827,41 +828,40 @@
       largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
   if (!paint.IsEmpty() &&
       WasStartedInForegroundOptionalEventInForeground(paint.Time(), info)) {
-    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentPaintAllFrames,
+    PAGE_LOAD_HISTOGRAM(internal::kHistogramLargestContentfulPaint,
                         paint.Time().value());
     UMA_HISTOGRAM_ENUMERATION(
-        internal::kHistogramLargestContentPaintAllFramesContentType,
-        paint.Type());
+        internal::kHistogramLargestContentfulPaintContentType, paint.Type());
   }
 
-  if (timing.paint_timing->first_paint &&
-      !timing.paint_timing->first_meaningful_paint) {
+  if (main_frame_timing.paint_timing->first_paint &&
+      !main_frame_timing.paint_timing->first_meaningful_paint) {
     RecordFirstMeaningfulPaintStatus(
-        timing.paint_timing->first_contentful_paint
+        main_frame_timing.paint_timing->first_contentful_paint
             ? internal::FIRST_MEANINGFUL_PAINT_DID_NOT_REACH_NETWORK_STABLE
             : internal::
                   FIRST_MEANINGFUL_PAINT_DID_NOT_REACH_FIRST_CONTENTFUL_PAINT);
   }
 
-  if (timing.paint_timing->first_paint &&
-      !timing.interactive_timing->interactive) {
+  if (main_frame_timing.paint_timing->first_paint &&
+      !main_frame_timing.interactive_timing->interactive) {
     RecordTimeToInteractiveStatus(
-        timing.paint_timing->first_meaningful_paint
+        main_frame_timing.paint_timing->first_meaningful_paint
             ? internal::TIME_TO_INTERACTIVE_DID_NOT_REACH_QUIESCENCE
             : internal::
                   TIME_TO_INTERACTIVE_DID_NOT_REACH_FIRST_MEANINGFUL_PAINT);
   }
 
-  if (timing.interactive_timing->longest_input_timestamp) {
-    DCHECK(timing.interactive_timing->longest_input_delay);
+  if (main_frame_timing.interactive_timing->longest_input_timestamp) {
+    DCHECK(main_frame_timing.interactive_timing->longest_input_delay);
     UMA_HISTOGRAM_CUSTOM_TIMES(
         internal::kHistogramLongestInputDelay,
-        timing.interactive_timing->longest_input_delay.value(),
+        main_frame_timing.interactive_timing->longest_input_delay.value(),
         base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(60),
         50);
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramLongestInputTimestamp,
-        timing.interactive_timing->longest_input_timestamp.value());
+        main_frame_timing.interactive_timing->longest_input_timestamp.value());
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
index 3973a52..1ac268a5 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h
@@ -32,10 +32,10 @@
 extern const char kHistogramFirstMeaningfulPaint[];
 extern const char kHistogramLargestImagePaint[];
 extern const char kHistogramLargestTextPaint[];
-extern const char kHistogramLargestContentPaint[];
-extern const char kHistogramLargestContentPaintContentType[];
-extern const char kHistogramLargestContentPaintAllFrames[];
-extern const char kHistogramLargestContentPaintAllFramesContentType[];
+extern const char kHistogramLargestContentfulPaint[];
+extern const char kHistogramLargestContentfulPaintContentType[];
+extern const char kHistogramLargestContentfulPaintMainFrame[];
+extern const char kHistogramLargestContentfulPaintMainFrameContentType[];
 extern const char kHistogramTimeToInteractive[];
 extern const char kHistogramParseDuration[];
 extern const char kHistogramParseBlockedOnScriptLoad[];
@@ -237,7 +237,7 @@
 
  private:
   void RecordTimingHistograms(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::mojom::PageLoadTiming& main_frame_timing,
       const page_load_metrics::PageLoadExtraInfo& info);
   void RecordByteAndResourceHistograms(
       const page_load_metrics::mojom::PageLoadTiming& timing,
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
index c55d71a..0475262 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
@@ -682,14 +682,23 @@
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestContentPaintAllFrames),
+                  internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintAllFramesContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
+  EXPECT_TRUE(
+      histogram_tester()
+          .GetAllSamples(internal::kHistogramLargestContentfulPaintMainFrame)
+          .empty());
+  EXPECT_TRUE(
+      histogram_tester()
+          .GetAllSamples(
+              internal::kHistogramLargestContentfulPaintMainFrameContentType)
+          .empty());
 }
 
 TEST_F(CorePageLoadMetricsObserverTest,
@@ -727,11 +736,20 @@
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestContentPaintAllFrames),
+                  internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintAllFramesContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
+      testing::ElementsAre(base::Bucket(
+          static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
+          1)));
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaintMainFrame),
+              testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(
+      histogram_tester().GetAllSamples(
+          internal::kHistogramLargestContentfulPaintMainFrameContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
@@ -750,7 +768,7 @@
   timing.navigation_start = base::Time::FromDoubleT(1);
   // Pick a value that lines up with a histogram bucket.
   timing.paint_timing->largest_image_paint =
-      base::TimeDelta::FromMilliseconds(10000);
+      base::TimeDelta::FromMilliseconds(9382);
   timing.paint_timing->largest_image_paint_size = 50u;
   PopulateRequiredTimingFields(&timing);
 
@@ -779,11 +797,21 @@
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestContentPaintAllFrames),
+                  internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintAllFramesContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
+      testing::ElementsAre(base::Bucket(
+          static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
+          1)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaintMainFrame),
+              testing::ElementsAre(base::Bucket(9382, 1)));
+  EXPECT_THAT(
+      histogram_tester().GetAllSamples(
+          internal::kHistogramLargestContentfulPaintMainFrameContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
@@ -830,11 +858,21 @@
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   EXPECT_THAT(histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestContentPaintAllFrames),
+                  internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintAllFramesContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
+      testing::ElementsAre(base::Bucket(
+          static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
+          1)));
+
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaintMainFrame),
+              testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(
+      histogram_tester().GetAllSamples(
+          internal::kHistogramLargestContentfulPaintMainFrameContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
           1)));
@@ -883,11 +921,11 @@
 
   // Ensure that the largest_image_paint timing for the main frame is recorded.
   EXPECT_THAT(histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestContentPaintAllFrames),
+                  internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintAllFramesContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
@@ -937,11 +975,11 @@
 
   // Ensure that the largest_image_paint timing for the main frame is recorded.
   EXPECT_THAT(histogram_tester().GetAllSamples(
-                  internal::kHistogramLargestContentPaintAllFrames),
+                  internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(990, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintAllFramesContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
@@ -1034,7 +1072,7 @@
       testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
-TEST_F(CorePageLoadMetricsObserverTest, LargestContentPaint_NoTextOrImage) {
+TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -1048,13 +1086,17 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramLargestContentPaint,
-                                      0);
   histogram_tester().ExpectTotalCount(
-      internal::kHistogramLargestContentPaintContentType, 0);
+      internal::kHistogramLargestContentfulPaint, 0);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaintContentType, 0);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaintMainFrame, 0);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
 }
 
-TEST_F(CorePageLoadMetricsObserverTest, LargestContentPaint_OnlyText) {
+TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyText) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -1069,18 +1111,18 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestContentPaint),
-      testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
           1)));
 }
 
-TEST_F(CorePageLoadMetricsObserverTest, LargestContentPaint_OnlyImage) {
+TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyImage) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -1095,19 +1137,19 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestContentPaint),
-      testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
 }
 
 TEST_F(CorePageLoadMetricsObserverTest,
-       LargestContentPaint_ImageLargerThanText) {
+       LargestContentfulPaint_ImageLargerThanText) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -1125,19 +1167,19 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestContentPaint),
-      testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
 }
 
 TEST_F(CorePageLoadMetricsObserverTest,
-       LargestContentPaint_TextLargerThanImage) {
+       LargestContentfulPaint_TextLargerThanImage) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -1155,12 +1197,12 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestContentPaint),
-      testing::ElementsAre(base::Bucket(990, 1)));
+  EXPECT_THAT(histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestContentfulPaint),
+              testing::ElementsAre(base::Bucket(990, 1)));
   EXPECT_THAT(
       histogram_tester().GetAllSamples(
-          internal::kHistogramLargestContentPaintContentType),
+          internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
           1)));
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index 37bae403..9a7d33f7 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -318,16 +318,15 @@
           &largest_content_paint_size, &largest_content_type) &&
       WasStartedInForegroundOptionalEventInForeground(
           largest_content_paint_time, info)) {
-    builder.SetExperimental_PaintTiming_NavigationToLargestContentPaint(
+    builder.SetPaintTiming_NavigationToLargestContentfulPaint_MainFrame(
         largest_content_paint_time.value().InMilliseconds());
   }
   const page_load_metrics::ContentfulPaintTimingInfo& paint =
       largest_contentful_paint_handler_.MergeMainFrameAndSubframes();
   if (!paint.IsEmpty() &&
       WasStartedInForegroundOptionalEventInForeground(paint.Time(), info)) {
-    builder
-        .SetExperimental_PaintTiming_NavigationToLargestContentPaintAllFrames(
-            paint.Time().value().InMilliseconds());
+    builder.SetPaintTiming_NavigationToLargestContentfulPaint(
+        paint.Time().value().InMilliseconds());
   }
   if (timing.interactive_timing->interactive) {
     base::TimeDelta time_to_interactive =
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index 454d7fd..939853b 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -423,7 +423,7 @@
   }
 }
 
-TEST_F(UkmPageLoadMetricsObserverTest, LargestContentPaint_Trace) {
+TEST_F(UkmPageLoadMetricsObserverTest, LargestContentfulPaint_Trace) {
   using trace_analyzer::Query;
   trace_analyzer::Start("*");
   {
@@ -465,7 +465,7 @@
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest,
-       LargestContentPaint_Trace_InvalidateCandidate) {
+       LargestContentfulPaint_Trace_InvalidateCandidate) {
   using trace_analyzer::Query;
   trace_analyzer::Start("loading");
   {
@@ -506,7 +506,7 @@
   EXPECT_EQ("loading", invalidate_events[0]->category);
 }
 
-TEST_F(UkmPageLoadMetricsObserverTest, LargestContentPaint_OnlyText) {
+TEST_F(UkmPageLoadMetricsObserverTest, LargestContentfulPaint_OnlyText) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -530,14 +530,13 @@
                                                 GURL(kTestUrl1));
     test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
-        PageLoad::kExperimental_PaintTiming_NavigationToLargestContentPaintName,
-        600);
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
 
-TEST_F(UkmPageLoadMetricsObserverTest, LargestContentPaint_OnlyImage) {
+TEST_F(UkmPageLoadMetricsObserverTest, LargestContentfulPaint_OnlyImage) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -561,15 +560,14 @@
                                                 GURL(kTestUrl1));
     test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
-        PageLoad::kExperimental_PaintTiming_NavigationToLargestContentPaintName,
-        600);
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest,
-       LargestContentPaint_ImageLargerThanText) {
+       LargestContentfulPaint_ImageLargerThanText) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -596,15 +594,14 @@
                                                 GURL(kTestUrl1));
     test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
-        PageLoad::kExperimental_PaintTiming_NavigationToLargestContentPaintName,
-        600);
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest,
-       LargestContentPaintAllFrames_OnlySubframe) {
+       LargestContentfulPaintAllFrames_OnlySubframe) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -642,16 +639,18 @@
                                                 GURL(kTestUrl1));
     test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 4780);
+    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+        kv.second.get(),
         PageLoad::
-            kExperimental_PaintTiming_NavigationToLargestContentPaintAllFramesName,
-        4780);
+            kPaintTiming_NavigationToLargestContentfulPaint_MainFrameName));
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest,
-       LargestContentPaintAllFrames_OnlyMainFrame) {
+       LargestContentfulPaintAllFrames_OnlyMainFrame) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -689,19 +688,21 @@
                                                 GURL(kTestUrl1));
     test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
-        PageLoad::
-            kExperimental_PaintTiming_NavigationToLargestContentPaintAllFramesName,
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 4780);
+    test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(),
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaint_MainFrameName,
         4780);
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
 
-// This is to test whether LargestContentPaintAllFrames can merge the candidates
-// from different frames correctly. The metric should pick the larger candidate
-// during merging.
+// This is to test whether LargestContentfulPaintAllFrames can merge the
+// candidates from different frames correctly. The metric should pick the larger
+// candidate during merging.
 TEST_F(UkmPageLoadMetricsObserverTest,
-       LargestContentPaintAllFrames_MergeFrameCandidateBySize) {
+       LargestContentfulPaintAllFrames_MergeFrameCandidateBySize) {
   page_load_metrics::mojom::PageLoadTiming timing;
   page_load_metrics::InitPageLoadTimingForTest(&timing);
   timing.navigation_start = base::Time::FromDoubleT(1);
@@ -742,9 +743,7 @@
                                                 GURL(kTestUrl1));
     test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
-        PageLoad::
-            kExperimental_PaintTiming_NavigationToLargestContentPaintAllFramesName,
-        990);
+        PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 990);
     EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index dacda5e..01b87efa 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -131,6 +131,7 @@
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
 #include "components/sync/base/sync_prefs.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "components/sync_sessions/session_sync_prefs.h"
 #include "components/translate/core/browser/translate_prefs.h"
@@ -743,6 +744,7 @@
   SessionStartupPref::RegisterProfilePrefs(registry);
   SharingSyncPreference::RegisterProfilePrefs(registry);
   sync_sessions::SessionSyncPrefs::RegisterProfilePrefs(registry);
+  syncer::DeviceInfoPrefs::RegisterProfilePrefs(registry);
   syncer::SyncPrefs::RegisterProfilePrefs(registry);
   syncer::PerUserTopicRegistrationManager::RegisterProfilePrefs(registry);
   syncer::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index 0dd0386..3c2845c 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -320,6 +320,7 @@
   WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
                       1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 1);
@@ -351,6 +352,7 @@
   WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
                       1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
   WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
   WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 0);
   WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
@@ -384,6 +386,43 @@
   WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
                       1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
+}
+
+// Test and Test Class for lightweight prefetch under the HTML+CSS+SyncScript
+// configuration.
+class HTMLCSSSyncScriptNoStatePrefetchBrowserTest
+    : public NoStatePrefetchBrowserTest {
+ public:
+  void SetUp() override {
+    std::map<std::string, std::string> parameters;
+    parameters["skip_other"] = "true";
+    parameters["skip_async_script"] = "true";
+    feature_list_.InitWithFeaturesAndParameters(
+        {{blink::features::kLightweightNoStatePrefetch, parameters}}, {});
+    NoStatePrefetchBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Checks that the expected resource types are fetched via NoState Prefetch.
+IN_PROC_BROWSER_TEST_F(HTMLCSSSyncScriptNoStatePrefetchBrowserTest,
+                       PrefetchHTMLCSSSyncScript) {
+  std::unique_ptr<TestPrerender> test_prerender =
+      PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
+                       FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
+
+  // Verify that the page load did not happen.
+  test_prerender->WaitForLoads(0);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
+                      1);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
   WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
   WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
@@ -418,6 +457,7 @@
   WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
                       1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
+  WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
   WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
   WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
diff --git a/chrome/browser/previews/previews_offline_helper.cc b/chrome/browser/previews/previews_offline_helper.cc
index 78bb794..fd7dc2d9 100644
--- a/chrome/browser/previews/previews_offline_helper.cc
+++ b/chrome/browser/previews/previews_offline_helper.cc
@@ -274,11 +274,10 @@
 
 void PreviewsOfflineHelper::OfflinePageDeleted(
     const offline_pages::OfflinePageItem& deleted_page) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Has no effect if the url was never in the dictionary.
-  available_pages_->RemoveKey(HashURL(deleted_page.url));
-  UpdatePref();
+  // Do nothing. OfflinePageModel calls |OfflinePageDeleted| when pages are
+  // refreshed, but because we only key on URL and not the offline page id, it
+  // is difficult to tell when this happens. So instead, it's ok if we
+  // over-trigger for a few pages until the next DB query.
 }
 
 void PreviewsOfflineHelper::UpdatePref() {
diff --git a/chrome/browser/previews/previews_offline_helper_unittest.cc b/chrome/browser/previews/previews_offline_helper_unittest.cc
index f4334ab..3e2b7ff1 100644
--- a/chrome/browser/previews/previews_offline_helper_unittest.cc
+++ b/chrome/browser/previews/previews_offline_helper_unittest.cc
@@ -52,12 +52,6 @@
     return item;
   }
 
-  offline_pages::OfflinePageItem MakeDeletedPageItem(const std::string& url) {
-    offline_pages::OfflinePageItem item;
-    item.url = GURL(url);  // Only |url| is needed.
-    return item;
-  }
-
  private:
   std::unique_ptr<PreviewsOfflineHelper> helper_;
 };
@@ -68,7 +62,6 @@
     bool enable_feature;
     std::vector<std::string> add_fresh_pages;
     std::vector<std::string> add_expired_pages;
-    std::vector<std::string> delete_pages;
     std::vector<std::string> want_pages;
     std::vector<std::string> not_want_pages;
     std::string original_url;
@@ -80,7 +73,6 @@
           .enable_feature = false,
           .add_fresh_pages = {},
           .add_expired_pages = {},
-          .delete_pages = {},
           .want_pages = {"http://chromium.org"},
           .not_want_pages = {},
           .original_url = "",
@@ -91,7 +83,6 @@
           .enable_feature = true,
           .add_fresh_pages = {},
           .add_expired_pages = {},
-          .delete_pages = {},
           .want_pages = {},
           .not_want_pages = {"http://chromium.org"},
           .original_url = "",
@@ -102,7 +93,6 @@
           .enable_feature = true,
           .add_fresh_pages = {"http://chromium.org"},
           .add_expired_pages = {},
-          .delete_pages = {},
           .want_pages = {"http://chromium.org"},
           .not_want_pages = {},
           .original_url = "",
@@ -113,7 +103,6 @@
           .enable_feature = true,
           .add_fresh_pages = {"http://chromium.org"},
           .add_expired_pages = {},
-          .delete_pages = {},
           .want_pages = {"http://google.com"},
           .not_want_pages = {},
           .original_url = "http://google.com",
@@ -124,18 +113,6 @@
           .enable_feature = true,
           .add_fresh_pages = {},
           .add_expired_pages = {"http://chromium.org"},
-          .delete_pages = {},
-          .want_pages = {},
-          .not_want_pages = {"http://chromium.org"},
-          .original_url = "",
-          .want_pref_size = 0,
-      },
-      {
-          .msg = "Added then deleted page returns false",
-          .enable_feature = true,
-          .add_fresh_pages = {"http://chromium.org"},
-          .add_expired_pages = {},
-          .delete_pages = {"http://chromium.org"},
           .want_pages = {},
           .not_want_pages = {"http://chromium.org"},
           .original_url = "",
@@ -146,7 +123,6 @@
           .enable_feature = true,
           .add_fresh_pages = {"http://chromium.org"},
           .add_expired_pages = {"http://chromium.org"},
-          .delete_pages = {},
           .want_pages = {"http://chromium.org"},
           .not_want_pages = {},
           .original_url = "",
@@ -157,7 +133,6 @@
           .enable_feature = true,
           .add_fresh_pages = {"http://chromium.org"},
           .add_expired_pages = {},
-          .delete_pages = {},
           .want_pages = {"http://chromium.org",
                          "http://chromium.org/#previews"},
           .not_want_pages = {},
@@ -167,13 +142,10 @@
       {
           .msg = "URLs with paths are different",
           .enable_feature = true,
-          .add_fresh_pages = {"http://chromium.org/fresh",
-                              "http://chromium.org/fresh_but_deleted"},
+          .add_fresh_pages = {"http://chromium.org/fresh"},
           .add_expired_pages = {"http://chromium.org/old"},
-          .delete_pages = {"http://chromium.org/fresh_but_deleted"},
           .want_pages = {"http://chromium.org/fresh"},
-          .not_want_pages = {"http://chromium.org/old",
-                             "http://chromium.org/fresh_but_deleted"},
+          .not_want_pages = {"http://chromium.org/old"},
           .original_url = "",
           .want_pref_size = 1,
       },
@@ -213,9 +185,6 @@
           nullptr,
           MakeAddedPageItem(fresh_page, test_case.original_url, fresh));
     }
-    for (const std::string& deleted_page : test_case.delete_pages) {
-      helper->OfflinePageDeleted(MakeDeletedPageItem(deleted_page));
-    }
 
     EXPECT_EQ(test_prefs.GetDictionary(kDictKey)->size(),
               test_case.want_pref_size);
diff --git a/chrome/browser/previews/previews_prober_browsertest.cc b/chrome/browser/previews/previews_prober_browsertest.cc
index acbe139a..dd16b80 100644
--- a/chrome/browser/previews/previews_prober_browsertest.cc
+++ b/chrome/browser/previews/previews_prober_browsertest.cc
@@ -16,6 +16,7 @@
 #include "content/public/browser/system_connector.h"
 #include "content/public/common/network_service_util.h"
 #include "content/public/common/service_names.mojom.h"
+#include "content/public/test/network_connection_change_simulator.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
@@ -33,20 +34,6 @@
   }
 }
 
-void SimulateNetworkChange(network::mojom::ConnectionType type) {
-  if (!content::IsInProcessNetworkService()) {
-    network::mojom::NetworkServiceTestPtr network_service_test;
-    content::GetSystemConnector()->BindInterface(
-        content::mojom::kNetworkServiceName, &network_service_test);
-    base::RunLoop run_loop;
-    network_service_test->SimulateNetworkChange(type, run_loop.QuitClosure());
-    run_loop.Run();
-    return;
-  }
-  net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
-      net::NetworkChangeNotifier::ConnectionType(type));
-}
-
 }  // namespace
 
 class TestDelegate : public PreviewsProber::Delegate {
@@ -165,14 +152,10 @@
   EXPECT_FALSE(prober.LastProbeWasSuccessful().value());
 }
 
-// TODO(crbug.com/985099): Test times out in component builds on Linux.
-#if defined(OS_LINUX) && defined(COMPONENT_BUILD)
-#define MAYBE_NetworkChange DISABLED_NetworkChange
-#else
-#define MAYBE_NetworkChange NetworkChange
-#endif
+IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, NetworkChange) {
+  content::NetworkConnectionChangeSimulator().SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_2G);
 
-IN_PROC_BROWSER_TEST_F(PreviewsProberBrowserTest, MAYBE_NetworkChange) {
   GURL url = TestURLWithPath("/ok");
   TestDelegate delegate;
   net::HttpRequestHeaders headers;
@@ -185,7 +168,9 @@
                         PreviewsProber::HttpMethod::kGet, headers, retry_policy,
                         timeout_policy, TRAFFIC_ANNOTATION_FOR_TESTS, 1,
                         base::TimeDelta::FromDays(1));
-  SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_4G);
+
+  content::NetworkConnectionChangeSimulator().SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_4G);
   WaitForCompletedProbe(&prober);
 
   EXPECT_TRUE(prober.LastProbeWasSuccessful().value());
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index c6028f0..bd27157 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -78,6 +78,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/search_engines/default_search_manager.h"
+#include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/chrome/browser/profiles/profile_metrics.cc b/chrome/browser/profiles/profile_metrics.cc
index 48ac2cb4..43638a1 100644
--- a/chrome/browser/profiles/profile_metrics.cc
+++ b/chrome/browser/profiles/profile_metrics.cc
@@ -22,6 +22,7 @@
 #include "chrome/installer/util/google_update_settings.h"
 #include "components/profile_metrics/browser_profile_type.h"
 #include "components/profile_metrics/counts.h"
+#include "components/signin/core/browser/signin_header_helper.h"
 #include "content/public/browser/browser_thread.h"
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/profiles/profile_metrics.h b/chrome/browser/profiles/profile_metrics.h
index 72fedfc..8b9760e 100644
--- a/chrome/browser/profiles/profile_metrics.h
+++ b/chrome/browser/profiles/profile_metrics.h
@@ -10,7 +10,6 @@
 
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 
 class Profile;
 class ProfileManager;
@@ -24,6 +23,10 @@
 struct Counts;
 }
 
+namespace signin {
+enum GAIAServiceType : int;
+}
+
 class ProfileMetrics {
  public:
 
diff --git a/chrome/browser/profiling_host/BUILD.gn b/chrome/browser/profiling_host/BUILD.gn
index 5c73c19..9fd1b0b4 100644
--- a/chrome/browser/profiling_host/BUILD.gn
+++ b/chrome/browser/profiling_host/BUILD.gn
@@ -19,7 +19,6 @@
     "//chrome/common:non_code_constants",
     "//components/heap_profiling",
     "//components/services/heap_profiling/public/cpp",
-    "//components/signin/core/browser",
     "//content/public/browser",
     "//content/public/common",
   ]
diff --git a/chrome/browser/resources/gaia_auth_host/authenticator.js b/chrome/browser/resources/gaia_auth_host/authenticator.js
index cd9a25c..dc1a35b 100644
--- a/chrome/browser/resources/gaia_auth_host/authenticator.js
+++ b/chrome/browser/resources/gaia_auth_host/authenticator.js
@@ -87,6 +87,9 @@
     'obfuscatedOwnerId',         // Obfuscated device owner ID, if needed.
     'extractSamlPasswordAttributes',  // If enabled attempts to extract password
                                       // attributes from the SAML response.
+    'ignoreCrOSIdpSetting',  // If set to true, causes Gaia to ignore 3P
+                             // SAML IdP SSO redirection policies (and
+                             // redirect to SAML IdPs by default).
 
     // The email fields allow for the following possibilities:
     //
@@ -572,6 +575,9 @@
         // argument to show an email domain.
         url = appendParam(url, 'hd', data.emailDomain);
       }
+      if (data.ignoreCrOSIdpSetting === true) {
+        url = appendParam(url, 'ignoreCrOSIdpSetting', 'true');
+      }
       return url;
     }
 
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index 8076a8b..63de1c0 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -104,9 +104,7 @@
   EDIT_BG: 'edit-bg',
   EDIT_BG_DIALOG: 'edit-bg-dialog',
   EDIT_BG_DIVIDER: 'edit-bg-divider',
-  EDIT_BG_ICON: 'edit-bg-icon',
   EDIT_BG_MENU: 'edit-bg-menu',
-  EDIT_BG_TEXT: 'edit-bg-text',
   MENU_BACK_CIRCLE: 'menu-back-circle',
   MENU_CANCEL: 'menu-cancel',
   MENU_DONE: 'menu-done',
@@ -142,8 +140,6 @@
   COLLECTION_TILE: 'bg-sel-tile',  // Preview tile for background customization
   COLLECTION_TILE_BG: 'bg-sel-tile-bg',
   COLLECTION_TITLE: 'bg-sel-tile-title',  // Title of a background image
-  // Extended and elevated style for entry point.
-  ENTRY_POINT_ENHANCED: 'ep-enhanced',
   IMAGE_DIALOG: 'is-img-sel',
   ON_IMAGE_MENU: 'on-img-menu',
   OPTION: 'bg-option',
diff --git a/chrome/browser/resources/local_ntp/externs.js b/chrome/browser/resources/local_ntp/externs.js
index 4669cc4..b56ce84 100644
--- a/chrome/browser/resources/local_ntp/externs.js
+++ b/chrome/browser/resources/local_ntp/externs.js
@@ -373,7 +373,6 @@
 configData.translatedStrings.connectionError;
 configData.translatedStrings.connectionErrorNoPeriod;
 configData.translatedStrings.copyLink;
-configData.translatedStrings.customizeButtonLabel;
 configData.translatedStrings.customizeThisPage;
 configData.translatedStrings.dailyRefresh;
 configData.translatedStrings.defaultWallpapers;
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index cfad34e..5c1b2430 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -700,9 +700,9 @@
               0 4px 8px 3px rgba(var(--GG800-rgb), .15);
   color: rgb(var(--GG700-rgb));
   height: 528px;
+  min-width: 800px;
   padding: 0;
   user-select: none;
-  width: 800px;
 }
 
 @media (prefers-color-scheme: dark) {
@@ -825,9 +825,8 @@
   height: 32px;
   line-height: 32px;
   margin-inline-start: 16px;
-  max-width: 128px;
+  max-width: 126px;
   overflow: hidden;
-  text-align: center;
   text-overflow: ellipsis;
   user-select: none;
   white-space: nowrap;
@@ -959,9 +958,9 @@
   background-color: unset;
   color: rgb(var(--GG700-rgb));
   font-size: 13px;
-  height: 30px;
-  margin-bottom: -28px;
-  padding: 8px 0 24px 0;
+  margin-bottom: -34px;
+  min-height: 30px;
+  padding: 0;
 }
 
 @media (prefers-color-scheme: dark) {
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index 9f2e45da..0ef21843 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -106,7 +106,7 @@
 
     <div id="edit-bg" tabindex="0" role="button" hidden>
       <div id="edit-bg-icon"></div>
-      <span id="edit-bg-text"></span>
+      <span id="edit-bg-text">$i18n{customizeButton}</span>
     </div>
 
     <a id="custom-bg-attr"></a>
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 24a75930..19192d1 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -81,6 +81,8 @@
   DARK: 'dark',
   DEFAULT_THEME: 'default-theme',
   DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide',
+  // Extended and elevated style for customization entry point.
+  ENTRY_POINT_ENHANCED: 'ep-enhanced',
   FAKEBOX_FOCUS: 'fakebox-focused',  // Applies focus styles to the fakebox
   // Applied when the fakebox placeholder text should not be hidden on focus.
   SHOW_PLACEHOLDER: 'show-placeholder',
@@ -430,8 +432,7 @@
 
   $(customize.IDS.EDIT_BG)
       .classList.toggle(
-          customize.CLASSES.ENTRY_POINT_ENHANCED,
-          !info.customBackgroundConfigured);
+          CLASSES.ENTRY_POINT_ENHANCED, !info.customBackgroundConfigured);
 
   if (configData.isGooglePage) {
     customize.onThemeChange();
@@ -1140,9 +1141,6 @@
     }
 
     doodles.init();
-
-    $(customize.IDS.EDIT_BG_TEXT).textContent =
-        configData.translatedStrings.customizeButtonLabel;
   } else {
     document.body.classList.add(CLASSES.NON_GOOGLE_PAGE);
   }
diff --git a/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chrome/browser/resources/plugin_metadata/plugins_linux.json
index 722e064f..d58562d 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_linux.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_linux.json
@@ -1,5 +1,5 @@
 {
-  "x-version": 40,
+  "x-version": 41,
   "adobe-flash-player": {
     "mime_types": [
       "application/futuresplash",
@@ -10,9 +10,9 @@
     ],
     "versions": [
       {
-        "version": "32.0.0.192",
+        "version": "32.0.0.207",
         "status": "up_to_date",
-        "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-26.html"
+        "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-30.html"
       }
     ],
     "lang": "en-US",
diff --git a/chrome/browser/resources/plugin_metadata/plugins_mac.json b/chrome/browser/resources/plugin_metadata/plugins_mac.json
index 7d0f49a..eb67de5 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_mac.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_mac.json
@@ -1,5 +1,5 @@
 {
-  "x-version": 46,
+  "x-version": 47,
   "adobe-flash-player": {
     "mime_types": [
       "application/futuresplash",
@@ -7,9 +7,9 @@
     ],
     "versions": [
       {
-        "version": "32.0.0.192",
+        "version": "32.0.0.207",
         "status": "requires_authorization",
-        "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-26.html"
+        "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-30.html"
       }
     ],
     "lang": "en-US",
diff --git a/chrome/browser/resources/plugin_metadata/plugins_win.json b/chrome/browser/resources/plugin_metadata/plugins_win.json
index 9abad05..ecf05d8 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_win.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_win.json
@@ -1,5 +1,5 @@
 {
-  "x-version": 55,
+  "x-version": 56,
   "adobe-flash-player": {
     "mime_types": [
       "application/futuresplash",
@@ -7,9 +7,9 @@
     ],
     "versions": [
       {
-        "version": "32.0.0.192",
+        "version": "32.0.0.207",
         "status": "requires_authorization",
-        "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-26.html"
+        "reference": "https://helpx.adobe.com/security/products/flash-player/apsb19-30.html"
       }
     ],
     "lang": "en-US",
diff --git a/chrome/browser/resources/print_preview/ui/pages_settings.js b/chrome/browser/resources/print_preview/ui/pages_settings.js
index f46015b..176161f 100644
--- a/chrome/browser/resources/print_preview/ui/pages_settings.js
+++ b/chrome/browser/resources/print_preview/ui/pages_settings.js
@@ -374,10 +374,8 @@
           'pageRangeSyntaxInstruction',
           loadTimeData.getString('examplePageRangeText'));
     } else {
-      formattedMessage = (this.pageCount === 0) ?
-          '' :
-          loadTimeData.getStringF(
-              'pageRangeLimitInstructionWithValue', this.pageCount);
+      formattedMessage = loadTimeData.getStringF(
+          'pageRangeLimitInstructionWithValue', this.pageCount);
     }
     return formattedMessage.replace(/<\/b>|<b>/g, '');
   },
@@ -404,6 +402,10 @@
    * @private
    */
   getAllPagesString_: function() {
+    if (this.pageCount === 0) {
+      return '';
+    }
+
     return this.pageCount === 1 ? '1' : `1-${this.pageCount}`;
   },
 
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index 6dac3dd..b94ace4 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -213,7 +213,6 @@
         "//components/safe_browsing:webprotect_proto",
         "//components/safe_browsing/db",
         "//components/security_interstitials/content:security_interstitial_page",
-        "//components/signin/core/browser",
         "//content/public/browser",
         "//net",
       ]
@@ -261,7 +260,6 @@
     "//components/prefs",
     "//components/safe_browsing/common",
     "//components/safe_browsing/common:safe_browsing_prefs",
-    "//components/signin/core/browser",
     "//components/signin/public/identity_manager",
     "//content/public/browser",
   ]
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
index 675b6c8..41c9558 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
@@ -55,14 +55,11 @@
   UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractSignatureFeaturesTime",
                       base::TimeTicks::Now() - start_time);
 
-  start_time = base::TimeTicks::Now();
   if (!binary_feature_extractor->ExtractImageFeatures(
           file_path, BinaryFeatureExtractor::kDefaultOptions,
           &results.image_headers, nullptr)) {
     results.image_headers = ClientDownloadRequest::ImageHeaders();
   }
-  UMA_HISTOGRAM_TIMES("SBClientDownload.ExtractImageHeadersTime",
-                      base::TimeTicks::Now() - start_time);
 
   return results;
 }
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index ff230b5..40e7d54 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -192,8 +192,6 @@
               IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MD);
 
     // Custom Backgrounds
-    AddString(translated_strings.get(), "customizeButtonLabel",
-              IDS_NTP_CUSTOMIZE_BUTTON_LABEL);
     AddString(translated_strings.get(), "defaultWallpapers",
               IDS_NTP_CUSTOM_BG_CHROME_WALLPAPERS);
     AddString(translated_strings.get(), "uploadImage",
@@ -978,6 +976,8 @@
 
     replacements["customizeMenu"] =
         l10n_util::GetStringUTF8(IDS_NTP_CUSTOM_BG_CUSTOMIZE_NTP_LABEL);
+    replacements["customizeButton"] =
+        l10n_util::GetStringUTF8(IDS_NTP_CUSTOMIZE_BUTTON_LABEL);
     replacements["cancelButton"] =
         l10n_util::GetStringUTF8(IDS_NTP_CUSTOM_BG_CANCEL);
     replacements["doneButton"] =
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
index 9afc8531..4a25b39f 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
@@ -12,13 +12,12 @@
 #include "base/json/json_writer.h"
 #include "base/strings/string_util.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/google/core/browser/google_url_tracker.h"
 #include "components/google/core/common/google_util.h"
-#include "components/signin/core/browser/chrome_connected_header_helper.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "content/public/browser/system_connector.h"
 #include "net/base/load_flags.h"
@@ -31,6 +30,11 @@
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
+#if defined(OS_CHROMEOS)
+#include "components/signin/core/browser/chrome_connected_header_helper.h"
+#include "components/signin/core/browser/signin_header_helper.h"
+#endif
+
 namespace {
 
 const char kNewTabOgbApiPath[] = "/async/newtab_ogb";
diff --git a/chrome/browser/search/promos/promo_service_factory.cc b/chrome/browser/search/promos/promo_service_factory.cc
index 1ea08fd..e6ef4572 100644
--- a/chrome/browser/search/promos/promo_service_factory.cc
+++ b/chrome/browser/search/promos/promo_service_factory.cc
@@ -17,9 +17,7 @@
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/chrome_features.h"
-#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/signin/core/browser/cookie_settings_util.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 
diff --git a/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc b/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc
index c17624a..79017df 100644
--- a/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc
+++ b/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc
@@ -17,8 +17,6 @@
 #include "chrome/common/webui_url_constants.h"
 #include "components/google/core/browser/google_url_tracker.h"
 #include "components/google/core/common/google_util.h"
-#include "components/signin/core/browser/chrome_connected_header_helper.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "content/public/browser/system_connector.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/search/search_suggest/search_suggest_loader_impl_unittest.cc b/chrome/browser/search/search_suggest/search_suggest_loader_impl_unittest.cc
index 5950459..bc55e75 100644
--- a/chrome/browser/search/search_suggest/search_suggest_loader_impl_unittest.cc
+++ b/chrome/browser/search/search_suggest/search_suggest_loader_impl_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/search/search_suggest/search_suggest_data.h"
 #include "components/google/core/browser/google_url_tracker.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_service_manager_context.h"
 #include "net/http/http_request_headers.h"
diff --git a/chrome/browser/search/search_suggest/search_suggest_service_factory.cc b/chrome/browser/search/search_suggest/search_suggest_service_factory.cc
index 62fbcf2..68e73ec 100644
--- a/chrome/browser/search/search_suggest/search_suggest_service_factory.cc
+++ b/chrome/browser/search/search_suggest/search_suggest_service_factory.cc
@@ -16,12 +16,10 @@
 #include "chrome/browser/search/ntp_features.h"
 #include "chrome/browser/search/search_suggest/search_suggest_loader_impl.h"
 #include "chrome/browser/search/search_suggest/search_suggest_service.h"
-#include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/signin/core/browser/cookie_settings_util.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index e095050..a373185 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -34,7 +34,6 @@
 #include "components/metrics/metrics_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/cookie_settings_util.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 03c83bf..e4e082b 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -34,8 +34,6 @@
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/common/url_constants.h"
 #include "components/signin/core/browser/account_reconcilor.h"
-#include "components/signin/core/browser/chrome_connected_header_helper.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/signin/public/base/account_consistency_method.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/signin/header_modification_delegate_on_ui_thread_impl.cc b/chrome/browser/signin/header_modification_delegate_on_ui_thread_impl.cc
index f9fcdf8..094975c 100644
--- a/chrome/browser/signin/header_modification_delegate_on_ui_thread_impl.cc
+++ b/chrome/browser/signin/header_modification_delegate_on_ui_thread_impl.cc
@@ -57,6 +57,10 @@
     const GURL& redirect_url) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   const PrefService* prefs = profile_->GetPrefs();
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  syncer::SyncService* sync_service =
+      ProfileSyncServiceFactory::GetForProfile(profile_);
+#endif
   FixAccountConsistencyRequestHeader(
       request_adapter, redirect_url, profile_->IsOffTheRecord(),
       prefs->GetInteger(prefs::kIncognitoModeAvailability),
@@ -66,8 +70,7 @@
       prefs->GetBoolean(prefs::kAccountConsistencyMirrorRequired),
 #endif
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
-      ProfileSyncServiceFactory::GetForProfile(profile_)
-          ->IsSyncFeatureEnabled(),
+      sync_service && sync_service->IsSyncFeatureEnabled(),
       prefs->GetString(prefs::kGoogleServicesSigninScopedDeviceId),
 #endif
       cookie_settings_.get());
diff --git a/chrome/browser/sync/device_info_sync_service_factory.cc b/chrome/browser/sync/device_info_sync_service_factory.cc
index cdb7188e..6fcf71c 100644
--- a/chrome/browser/sync/device_info_sync_service_factory.cc
+++ b/chrome/browser/sync/device_info_sync_service_factory.cc
@@ -22,6 +22,7 @@
 #include "components/send_tab_to_self/features.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/model/model_type_store_service.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_device_info/device_info_sync_service_impl.h"
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 
@@ -101,7 +102,11 @@
           chrome::GetChannel(), chrome::GetVersionString(),
           signin_scoped_device_id_callback,
           send_tab_to_self_receiving_enabled_callback);
+
+  auto device_prefs =
+      std::make_unique<syncer::DeviceInfoPrefs>(profile->GetPrefs());
+
   return new syncer::DeviceInfoSyncServiceImpl(
       ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory(),
-      std::move(local_device_info_provider));
+      std::move(local_device_info_provider), std::move(device_prefs));
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 4296454..d8080196 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -241,6 +241,8 @@
     "webui/fileicon_source.h",
     "webui/flags_ui.cc",
     "webui/flags_ui.h",
+    "webui/flags_ui_handler.cc",
+    "webui/flags_ui_handler.h",
     "webui/gcm_internals_ui.cc",
     "webui/gcm_internals_ui.h",
     "webui/interstitials/interstitial_ui.cc",
diff --git a/chrome/browser/ui/app_list/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service_app_item.cc
index 5332eda..6458d9e6 100644
--- a/chrome/browser/ui/app_list/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service_app_item.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "base/bind.h"
+#include "base/logging.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
@@ -43,6 +44,10 @@
     case apps::mojom::AppType::kWeb:
       return std::make_unique<app_list::ExtensionAppContextMenu>(
           delegate, profile, app_id, controller, is_platform_app);
+
+    case apps::mojom::AppType::kMacNative:
+      NOTREACHED() << "Should not be trying to make a menu for a native app";
+      return nullptr;
   }
 
   return nullptr;
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index a407f6a2..90f0e9d 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -51,7 +51,6 @@
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_requirements_service.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 6445a0f..38fe7bc8 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -22,7 +22,6 @@
 #include "chrome/browser/ui/in_product_help/in_product_help.h"
 #include "chrome/common/buildflags.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/translate/core/common/translate_errors.h"
 #include "ui/base/base_window.h"
 #include "ui/base/window_open_disposition.h"
@@ -67,6 +66,10 @@
 class Size;
 }
 
+namespace signin {
+struct ManageAccountsParams;
+}
+
 namespace signin_metrics {
 enum class AccessPoint;
 }
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index d0aa8a9..f9ccd4ae 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -76,11 +76,9 @@
  public:
   IntentPickerLabelButton(views::ButtonListener* listener,
                           const gfx::Image* icon,
-                          const std::string& launch_name,
                           const std::string& display_name)
       : LabelButton(listener,
-                    base::UTF8ToUTF16(base::StringPiece(display_name))),
-        launch_name_(launch_name) {
+                    base::UTF8ToUTF16(base::StringPiece(display_name))) {
     SetHorizontalAlignment(gfx::ALIGN_LEFT);
     SetMinSize(gfx::Size(kMaxIntentPickerLabelButtonWidth, kRowHeight));
     SetInkDropMode(InkDropMode::ON);
@@ -106,9 +104,6 @@
     return GetInkDrop()->GetTargetInkDropState();
   }
 
- private:
-  std::string launch_name_;
-
   DISALLOW_COPY_AND_ASSIGN(IntentPickerLabelButton);
 };
 
@@ -353,7 +348,7 @@
     }
 #endif  // defined(OS_CHROMEOS)
     auto app_button = std::make_unique<IntentPickerLabelButton>(
-        this, &app_info.icon, app_info.launch_name, app_info.display_name);
+        this, &app_info.icon, app_info.display_name);
     app_button->set_tag(i);
     scrollable_view->AddChildViewAt(std::move(app_button), i++);
   }
diff --git a/chrome/browser/ui/views/location_bar/intent_picker_view.cc b/chrome/browser/ui/views/location_bar/intent_picker_view.cc
index d6712a0..46ae43b 100644
--- a/chrome/browser/ui/views/location_bar/intent_picker_view.cc
+++ b/chrome/browser/ui/views/location_bar/intent_picker_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/location_bar/intent_picker_view.h"
 
+#include "build/build_config.h"
 #include "chrome/browser/apps/intent_helper/apps_navigation_throttle.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
@@ -19,6 +20,10 @@
 #include "chrome/browser/chromeos/apps/intent_helper/chromeos_apps_navigation_throttle.h"
 #endif  //  defined(OS_CHROMEOS)
 
+#if defined(OS_MACOSX)
+#include "chrome/browser/apps/intent_helper/mac_apps_navigation_throttle.h"
+#endif  //  defined(OS_MACOSX)
+
 namespace content {
 class WebContents;
 }
@@ -48,6 +53,9 @@
 #if defined(OS_CHROMEOS)
   chromeos::ChromeOsAppsNavigationThrottle::ShowIntentPickerBubble(
       web_contents, /*ui_auto_display_service=*/nullptr, url);
+#elif defined(OS_MACOSX)
+  apps::MacAppsNavigationThrottle::ShowIntentPickerBubble(
+      web_contents, /*ui_auto_display_service=*/nullptr, url);
 #else
   apps::AppsNavigationThrottle::ShowIntentPickerBubble(
       web_contents, /*ui_auto_display_service=*/nullptr, url);
diff --git a/chrome/browser/ui/webui/flags_ui.cc b/chrome/browser/ui/webui/flags_ui.cc
index ccb06ed..cdf598e 100644
--- a/chrome/browser/ui/webui/flags_ui.cc
+++ b/chrome/browser/ui/webui/flags_ui.cc
@@ -14,12 +14,9 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "build/build_config.h"
-#include "chrome/browser/about_flags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/channel_info.h"
+#include "chrome/browser/ui/webui/flags_ui_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "components/flags_ui/flags_ui_constants.h"
@@ -85,195 +82,6 @@
   return source;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-//
-// FlagsDOMHandler
-//
-////////////////////////////////////////////////////////////////////////////////
-
-// The handler for Javascript messages for the about:flags page.
-class FlagsDOMHandler : public WebUIMessageHandler {
- public:
-  FlagsDOMHandler() : access_(flags_ui::kGeneralAccessFlagsOnly),
-                      experimental_features_requested_(false) {
-  }
-  ~FlagsDOMHandler() override {}
-
-  // Initializes the DOM handler with the provided flags storage and flags
-  // access. If there were flags experiments requested from javascript before
-  // this was called, it calls |HandleRequestExperimentalFeatures| again.
-  void Init(flags_ui::FlagsStorage* flags_storage,
-            flags_ui::FlagAccess access);
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override;
-
-  // Callback for the "requestExperimentFeatures" message.
-  void HandleRequestExperimentalFeatures(const base::ListValue* args);
-
-  // Callback for the "enableExperimentalFeature" message.
-  void HandleEnableExperimentalFeatureMessage(const base::ListValue* args);
-
-  // Callback for the "setOriginListFlag" message.
-  void HandleSetOriginListFlagMessage(const base::ListValue* args);
-
-  // Callback for the "restartBrowser" message. Restores all tabs on restart.
-  void HandleRestartBrowser(const base::ListValue* args);
-
-  // Callback for the "resetAllFlags" message.
-  void HandleResetAllFlags(const base::ListValue* args);
-
- private:
-  std::unique_ptr<flags_ui::FlagsStorage> flags_storage_;
-  flags_ui::FlagAccess access_;
-  bool experimental_features_requested_;
-
-  DISALLOW_COPY_AND_ASSIGN(FlagsDOMHandler);
-};
-
-void FlagsDOMHandler::RegisterMessages() {
-  web_ui()->RegisterMessageCallback(
-      flags_ui::kRequestExperimentalFeatures,
-      base::BindRepeating(&FlagsDOMHandler::HandleRequestExperimentalFeatures,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      flags_ui::kEnableExperimentalFeature,
-      base::BindRepeating(
-          &FlagsDOMHandler::HandleEnableExperimentalFeatureMessage,
-          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      flags_ui::kSetOriginListFlag,
-      base::BindRepeating(&FlagsDOMHandler::HandleSetOriginListFlagMessage,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      flags_ui::kRestartBrowser,
-      base::BindRepeating(&FlagsDOMHandler::HandleRestartBrowser,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      flags_ui::kResetAllFlags,
-      base::BindRepeating(&FlagsDOMHandler::HandleResetAllFlags,
-                          base::Unretained(this)));
-}
-
-void FlagsDOMHandler::Init(flags_ui::FlagsStorage* flags_storage,
-                           flags_ui::FlagAccess access) {
-  flags_storage_.reset(flags_storage);
-  access_ = access;
-
-  if (experimental_features_requested_)
-    HandleRequestExperimentalFeatures(nullptr);
-}
-
-void FlagsDOMHandler::HandleRequestExperimentalFeatures(
-    const base::ListValue* args) {
-  experimental_features_requested_ = true;
-  // Bail out if the handler hasn't been initialized yet. The request will be
-  // handled after the initialization.
-  if (!flags_storage_)
-    return;
-
-  base::DictionaryValue results;
-
-  std::unique_ptr<base::ListValue> supported_features(new base::ListValue);
-  std::unique_ptr<base::ListValue> unsupported_features(new base::ListValue);
-  about_flags::GetFlagFeatureEntries(flags_storage_.get(),
-                                     access_,
-                                     supported_features.get(),
-                                     unsupported_features.get());
-  results.Set(flags_ui::kSupportedFeatures, std::move(supported_features));
-  results.Set(flags_ui::kUnsupportedFeatures, std::move(unsupported_features));
-  results.SetBoolean(flags_ui::kNeedsRestart,
-                     about_flags::IsRestartNeededToCommitChanges());
-  results.SetBoolean(flags_ui::kShowOwnerWarning,
-                     access_ == flags_ui::kGeneralAccessFlagsOnly);
-
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
-  version_info::Channel channel = chrome::GetChannel();
-  results.SetBoolean(flags_ui::kShowBetaChannelPromotion,
-                     channel == version_info::Channel::STABLE);
-  results.SetBoolean(flags_ui::kShowDevChannelPromotion,
-                     channel == version_info::Channel::BETA);
-#else
-  results.SetBoolean(flags_ui::kShowBetaChannelPromotion, false);
-  results.SetBoolean(flags_ui::kShowDevChannelPromotion, false);
-#endif
-  web_ui()->CallJavascriptFunctionUnsafe(flags_ui::kReturnExperimentalFeatures,
-                                         results);
-}
-
-void FlagsDOMHandler::HandleEnableExperimentalFeatureMessage(
-    const base::ListValue* args) {
-  DCHECK(flags_storage_);
-  DCHECK_EQ(2u, args->GetSize());
-  if (args->GetSize() != 2)
-    return;
-
-  std::string entry_internal_name;
-  std::string enable_str;
-  if (!args->GetString(0, &entry_internal_name) ||
-      !args->GetString(1, &enable_str))
-    return;
-
-  about_flags::SetFeatureEntryEnabled(flags_storage_.get(), entry_internal_name,
-                                      enable_str == "true");
-}
-
-void FlagsDOMHandler::HandleSetOriginListFlagMessage(
-    const base::ListValue* args) {
-  DCHECK(flags_storage_);
-  if (args->GetSize() != 2) {
-    NOTREACHED();
-    return;
-  }
-
-  std::string entry_internal_name;
-  std::string value_str;
-  if (!args->GetString(0, &entry_internal_name) ||
-      !args->GetString(1, &value_str) || entry_internal_name.empty()) {
-    NOTREACHED();
-    return;
-  }
-
-  about_flags::SetOriginListFlag(entry_internal_name, value_str,
-                                 flags_storage_.get());
-}
-
-void FlagsDOMHandler::HandleRestartBrowser(const base::ListValue* args) {
-  DCHECK(flags_storage_);
-#if defined(OS_CHROMEOS)
-  // On ChromeOS be less intrusive and restart inside the user session after
-  // we apply the newly selected flags.
-  base::CommandLine user_flags(base::CommandLine::NO_PROGRAM);
-  about_flags::ConvertFlagsToSwitches(flags_storage_.get(),
-                                      &user_flags,
-                                      flags_ui::kAddSentinels);
-
-  // Adhere to policy-enforced command-line switch handling when
-  // applying modified flags..
-  chromeos::UserSessionManager::ApplyUserPolicyToSwitches(
-      Profile::FromWebUI(web_ui())->GetPrefs(), &user_flags);
-
-  base::CommandLine::StringVector flags;
-  // argv[0] is the program name |base::CommandLine::NO_PROGRAM|.
-  flags.assign(user_flags.argv().begin() + 1, user_flags.argv().end());
-  VLOG(1) << "Restarting to apply per-session flags...";
-  AccountId account_id =
-      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId();
-  chromeos::UserSessionManager::GetInstance()->SetSwitchesForUser(
-      account_id,
-      chromeos::UserSessionManager::CommandLineSwitchesType::
-          kPolicyAndFlagsAndKioskControl,
-      flags);
-#endif
-  chrome::AttemptRestart();
-}
-
-void FlagsDOMHandler::HandleResetAllFlags(const base::ListValue* args) {
-  DCHECK(flags_storage_);
-  about_flags::ResetAllFlags(flags_storage_.get());
-}
-
-
 #if defined(OS_CHROMEOS)
 // On ChromeOS verifying if the owner is signed in is async operation and only
 // after finishing it the UI can be properly populated. This function is the
@@ -281,7 +89,7 @@
 // proper PrefService for the flags interface.
 void FinishInitialization(base::WeakPtr<FlagsUI> flags_ui,
                           Profile* profile,
-                          FlagsDOMHandler* dom_handler,
+                          FlagsUIHandler* dom_handler,
                           bool current_user_is_owner) {
   DCHECK(!profile->IsOffTheRecord());
   // If the flags_ui has gone away, there's nothing to do.
@@ -309,17 +117,11 @@
 
 }  // namespace
 
-///////////////////////////////////////////////////////////////////////////////
-//
-// FlagsUI
-//
-///////////////////////////////////////////////////////////////////////////////
-
 FlagsUI::FlagsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
   Profile* profile = Profile::FromWebUI(web_ui);
 
-  auto handler_owner = std::make_unique<FlagsDOMHandler>();
-  FlagsDOMHandler* handler = handler_owner.get();
+  auto handler_owner = std::make_unique<FlagsUIHandler>();
+  FlagsUIHandler* handler = handler_owner.get();
   web_ui->AddMessageHandler(std::move(handler_owner));
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/flags_ui_handler.cc b/chrome/browser/ui/webui/flags_ui_handler.cc
new file mode 100644
index 0000000..0c03c7a
--- /dev/null
+++ b/chrome/browser/ui/webui/flags_ui_handler.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/flags_ui_handler.h"
+
+#include "base/bind.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
+#include "chrome/common/channel_info.h"
+#include "components/flags_ui/flags_storage.h"
+#include "components/flags_ui/flags_ui_constants.h"
+#include "components/version_info/channel.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/system/sys_info.h"
+#include "chrome/browser/chromeos/login/session/user_session_manager.h"
+#include "components/account_id/account_id.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/user_manager/user_manager.h"
+#endif
+
+FlagsUIHandler::FlagsUIHandler()
+    : access_(flags_ui::kGeneralAccessFlagsOnly),
+      experimental_features_requested_(false) {}
+
+FlagsUIHandler::~FlagsUIHandler() {}
+
+void FlagsUIHandler::RegisterMessages() {
+  web_ui()->RegisterMessageCallback(
+      flags_ui::kRequestExperimentalFeatures,
+      base::BindRepeating(&FlagsUIHandler::HandleRequestExperimentalFeatures,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      flags_ui::kEnableExperimentalFeature,
+      base::BindRepeating(
+          &FlagsUIHandler::HandleEnableExperimentalFeatureMessage,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      flags_ui::kSetOriginListFlag,
+      base::BindRepeating(&FlagsUIHandler::HandleSetOriginListFlagMessage,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      flags_ui::kRestartBrowser,
+      base::BindRepeating(&FlagsUIHandler::HandleRestartBrowser,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      flags_ui::kResetAllFlags,
+      base::BindRepeating(&FlagsUIHandler::HandleResetAllFlags,
+                          base::Unretained(this)));
+}
+
+void FlagsUIHandler::Init(flags_ui::FlagsStorage* flags_storage,
+                          flags_ui::FlagAccess access) {
+  flags_storage_.reset(flags_storage);
+  access_ = access;
+
+  if (experimental_features_requested_)
+    HandleRequestExperimentalFeatures(nullptr);
+}
+
+void FlagsUIHandler::HandleRequestExperimentalFeatures(
+    const base::ListValue* args) {
+  experimental_features_requested_ = true;
+  // Bail out if the handler hasn't been initialized yet. The request will be
+  // handled after the initialization.
+  if (!flags_storage_)
+    return;
+
+  base::DictionaryValue results;
+
+  std::unique_ptr<base::ListValue> supported_features(new base::ListValue);
+  std::unique_ptr<base::ListValue> unsupported_features(new base::ListValue);
+
+  about_flags::GetFlagFeatureEntries(flags_storage_.get(), access_,
+                                     supported_features.get(),
+                                     unsupported_features.get());
+
+  results.Set(flags_ui::kSupportedFeatures, std::move(supported_features));
+  results.Set(flags_ui::kUnsupportedFeatures, std::move(unsupported_features));
+  results.SetBoolean(flags_ui::kNeedsRestart,
+                     about_flags::IsRestartNeededToCommitChanges());
+  results.SetBoolean(flags_ui::kShowOwnerWarning,
+                     access_ == flags_ui::kGeneralAccessFlagsOnly);
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+  version_info::Channel channel = chrome::GetChannel();
+  results.SetBoolean(flags_ui::kShowBetaChannelPromotion,
+                     channel == version_info::Channel::STABLE);
+  results.SetBoolean(flags_ui::kShowDevChannelPromotion,
+                     channel == version_info::Channel::BETA);
+#else
+  results.SetBoolean(flags_ui::kShowBetaChannelPromotion, false);
+  results.SetBoolean(flags_ui::kShowDevChannelPromotion, false);
+#endif
+  web_ui()->CallJavascriptFunctionUnsafe(flags_ui::kReturnExperimentalFeatures,
+                                         results);
+}
+
+void FlagsUIHandler::HandleEnableExperimentalFeatureMessage(
+    const base::ListValue* args) {
+  DCHECK(flags_storage_);
+  DCHECK_EQ(2u, args->GetSize());
+  if (args->GetSize() != 2)
+    return;
+
+  std::string entry_internal_name;
+  std::string enable_str;
+  if (!args->GetString(0, &entry_internal_name) ||
+      !args->GetString(1, &enable_str))
+    return;
+
+  about_flags::SetFeatureEntryEnabled(flags_storage_.get(), entry_internal_name,
+                                      enable_str == "true");
+}
+
+void FlagsUIHandler::HandleSetOriginListFlagMessage(
+    const base::ListValue* args) {
+  DCHECK(flags_storage_);
+  if (args->GetSize() != 2) {
+    NOTREACHED();
+    return;
+  }
+
+  std::string entry_internal_name;
+  std::string value_str;
+  if (!args->GetString(0, &entry_internal_name) ||
+      !args->GetString(1, &value_str) || entry_internal_name.empty()) {
+    NOTREACHED();
+    return;
+  }
+
+  about_flags::SetOriginListFlag(entry_internal_name, value_str,
+                                 flags_storage_.get());
+}
+
+void FlagsUIHandler::HandleRestartBrowser(const base::ListValue* args) {
+  DCHECK(flags_storage_);
+#if defined(OS_CHROMEOS)
+  // On ChromeOS be less intrusive and restart inside the user session after
+  // we apply the newly selected flags.
+  base::CommandLine user_flags(base::CommandLine::NO_PROGRAM);
+  about_flags::ConvertFlagsToSwitches(flags_storage_.get(), &user_flags,
+                                      flags_ui::kAddSentinels);
+
+  // Adhere to policy-enforced command-line switch handling when
+  // applying modified flags..
+  chromeos::UserSessionManager::ApplyUserPolicyToSwitches(
+      Profile::FromWebUI(web_ui())->GetPrefs(), &user_flags);
+
+  base::CommandLine::StringVector flags;
+  // argv[0] is the program name |base::CommandLine::NO_PROGRAM|.
+  flags.assign(user_flags.argv().begin() + 1, user_flags.argv().end());
+  VLOG(1) << "Restarting to apply per-session flags...";
+  AccountId account_id =
+      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId();
+  chromeos::UserSessionManager::GetInstance()->SetSwitchesForUser(
+      account_id,
+      chromeos::UserSessionManager::CommandLineSwitchesType::
+          kPolicyAndFlagsAndKioskControl,
+      flags);
+#endif
+  chrome::AttemptRestart();
+}
+
+void FlagsUIHandler::HandleResetAllFlags(const base::ListValue* args) {
+  DCHECK(flags_storage_);
+  about_flags::ResetAllFlags(flags_storage_.get());
+}
diff --git a/chrome/browser/ui/webui/flags_ui_handler.h b/chrome/browser/ui/webui/flags_ui_handler.h
new file mode 100644
index 0000000..05c6bfd
--- /dev/null
+++ b/chrome/browser/ui/webui/flags_ui_handler.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/flags_ui.h"
+
+#include "build/build_config.h"
+#include "components/flags_ui/feature_entry.h"
+#include "components/flags_ui/flags_state.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+#ifndef CHROME_BROWSER_UI_WEBUI_FLAGS_UI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_FLAGS_UI_HANDLER_H_
+
+namespace flags_ui {
+class FlagsStorage;
+}
+
+class FlagsUIHandler : public content::WebUIMessageHandler {
+ public:
+  FlagsUIHandler();
+  ~FlagsUIHandler() override;
+
+  // Initializes the UI handler with the provided flags storage and flags
+  // access. If there were flags experiments requested from javascript before
+  // this was called, it calls |HandleRequestExperimentalFeatures| again.
+  void Init(flags_ui::FlagsStorage* flags_storage, flags_ui::FlagAccess access);
+
+  // WebUIMessageHandler implementation.
+  void RegisterMessages() override;
+
+  // Callback for the "requestExperimentFeatures" message.
+  void HandleRequestExperimentalFeatures(const base::ListValue* args);
+
+  // Callback for the "enableExperimentalFeature" message.
+  void HandleEnableExperimentalFeatureMessage(const base::ListValue* args);
+
+  // Callback for the "setOriginListFlag" message.
+  void HandleSetOriginListFlagMessage(const base::ListValue* args);
+
+  // Callback for the "restartBrowser" message. Restores all tabs on restart.
+  void HandleRestartBrowser(const base::ListValue* args);
+
+  // Callback for the "resetAllFlags" message.
+  void HandleResetAllFlags(const base::ListValue* args);
+
+ private:
+  std::unique_ptr<flags_ui::FlagsStorage> flags_storage_;
+  flags_ui::FlagAccess access_;
+  bool experimental_features_requested_;
+
+  DISALLOW_COPY_AND_ASSIGN(FlagsUIHandler);
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_FLAGS_UI_HANDLER_H_
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
index dd52c8a..3e034ca3 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
@@ -124,6 +124,12 @@
   params.SetKey("constrained", base::Value("1"));
   params.SetKey("flow", base::Value("crosAddAccount"));
   params.SetBoolean("dontResizeNonEmbeddedPages", true);
+
+  // For in-session login flows, request Gaia to ignore third party SAML IdP SSO
+  // redirection policies (and redirect to SAML IdPs by default), otherwise some
+  // managed users will not be able to login to Chrome OS at all. Please check
+  // https://crbug.com/984525 and https://crbug.com/984525#c20 for more context.
+  params.SetBoolean("ignoreCrOSIdpSetting", true);
 }
 
 void InlineLoginHandlerChromeOS::CompleteLogin(const std::string& email,
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index 023d1b5e..201e1922 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -58,8 +58,6 @@
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/about_signin_internals.h"
-#include "components/signin/core/browser/signin_header_helper.h"
-#include "components/signin/core/browser/signin_investigator.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/accounts_cookie_mutator.h"
diff --git a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
index b029534..dec3301d 100644
--- a/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
+++ b/chrome/browser/ui/webui/signin/signin_create_profile_handler.cc
@@ -37,7 +37,6 @@
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_error_controller.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/ui/webui/signin_internals_ui.cc b/chrome/browser/ui/webui/signin_internals_ui.cc
index 45574c4..6181aae01 100644
--- a/chrome/browser/ui/webui/signin_internals_ui.cc
+++ b/chrome/browser/ui/webui/signin_internals_ui.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/common/url_constants.h"
 #include "components/grit/components_resources.h"
-#include "components/signin/core/browser/about_signin_internals.h"
 #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 20218cf..eb5041d 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -158,7 +158,8 @@
   },
   "browserAction": {
     "dependencies": ["manifest:browser_action"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   },
   // This API is whitelisted on stable and should not be enabled for a wider
   // audience without resolving security issues raised in API proposal and
@@ -262,7 +263,8 @@
   },
   "contextMenus": {
     "dependencies": ["permission:contextMenus"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   },
   "contextMenusInternal": {
     "internal": true,
@@ -393,7 +395,8 @@
     "contexts": ["blessed_extension"]
   },
   "extension.getURL": {
-    "contexts": ["blessed_extension", "unblessed_extension", "content_script", "extension_service_worker"]
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "disallow_for_service_workers": false
   },
   "extension.getViews": [
     {
@@ -708,7 +711,8 @@
   "tabs": [{
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   }, {
     "channel": "stable",
     "contexts": ["webui"],
@@ -769,7 +773,8 @@
   },
   "webNavigation": {
     "dependencies": ["permission:webNavigation"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   },
   "webrtcAudioPrivate": {
     "dependencies": ["permission:webrtcAudioPrivate"],
diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_observer.cc
index 33a76ca8..0665ee0c 100644
--- a/chrome/renderer/content_settings_observer.cc
+++ b/chrome/renderer/content_settings_observer.cc
@@ -420,7 +420,8 @@
       extension_dispatcher_->script_context_set().GetCurrent();
   if (current_context) {
     if (current_context->effective_context_type() ==
-        extensions::Feature::BLESSED_EXTENSION_CONTEXT) {
+            extensions::Feature::BLESSED_EXTENSION_CONTEXT &&
+        !current_context->IsForServiceWorker()) {
       allowed = true;
     } else {
       allowed |= current_context->HasAPIPermission(
diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
index 175abc04..216f9623 100644
--- a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
+++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
@@ -232,10 +232,10 @@
     case extensions::Feature::WEB_PAGE_CONTEXT:
     case extensions::Feature::UNBLESSED_EXTENSION_CONTEXT:
     case extensions::Feature::WEBUI_CONTEXT:
-    case extensions::Feature::SERVICE_WORKER_CONTEXT:
     case extensions::Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
       return false;
     case extensions::Feature::BLESSED_EXTENSION_CONTEXT:
+      return !current_context->IsForServiceWorker();
     case extensions::Feature::CONTENT_SCRIPT_CONTEXT:
       return true;
     case extensions::Feature::BLESSED_WEB_PAGE_CONTEXT:
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom
index 0ac0756..4223f916 100644
--- a/chrome/services/app_service/public/mojom/types.mojom
+++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -67,9 +67,10 @@
   kUnknown = 0,
   kArc,        // Android app.
   kBuiltIn,    // Built-in app.
-  kCrostini,   // Linux app.
+  kCrostini,   // Linux (via Crostini) app.
   kExtension,  // Extension-backed app.
   kWeb,        // Web app.
+  kMacNative,  // Native Mac app.
 };
 
 // Whether an app is ready to launch, i.e. installed.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index a7953e84..8dc72ce1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -657,6 +657,9 @@
       "//components/resources",
       "//components/safe_browsing/db:test_database_manager",
       "//components/services/quarantine:test_support",
+      "//components/signin/core/browser",
+      "//components/signin/public/base:signin_buildflags",
+      "//components/signin/public/identity_manager",
       "//components/spellcheck:buildflags",
       "//components/strings",
       "//components/sync",
@@ -1373,7 +1376,6 @@
         "//components/arc:arc_test_support",
         "//components/exo:test_support",
         "//components/prefs",
-        "//components/signin/public/identity_manager",
         "//components/user_manager:test_support",
         "//content/public/common:feature_h264_with_openh264_ffmpeg",
         "//mojo/core/embedder",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 21216733..02f735e 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -20,6 +20,8 @@
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/SuggestionTileController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/PageController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherMenuController.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/controllers/urlpage/UrlPage.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiAutomatorTestRule.java",
@@ -56,6 +58,7 @@
     "javatests/src/org/chromium/chrome/test/pagecontroller/tests/ExampleTest.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/tests/FirstRunControllerTest.java",
     "javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java",
+    "javatests/src/org/chromium/chrome/test/pagecontroller/tests/TabSwitcherControllerTest.java",
   ]
   deps = [
     ":chrome_java_test_pagecontroller",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java
index 27120579..2dd79933 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/ntp/NewTabPageController.java
@@ -6,6 +6,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.controllers.tabswitcher.TabSwitcherController;
 import org.chromium.chrome.test.pagecontroller.controllers.urlpage.UrlPage;
 import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
 import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
@@ -246,6 +247,18 @@
     }
 
     /**
+     * Open the tab switcher at the top.  This will cause the page to scroll to the top.
+     * @return The TabSwitcher Page Controller.
+     */
+    public TabSwitcherController openTabSwitcher() {
+        scrollToTop();
+        mUtils.click(LOCATOR_TAB_SWITCHER);
+        TabSwitcherController inst = TabSwitcherController.getInstance();
+        inst.verify();
+        return inst;
+    }
+
+    /**
      * Open the 3-dot menu at the top.  This will cause the page to scroll to the top.
      * @return The ChromeMenu Page Controller.
      */
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
new file mode 100644
index 0000000..a257204
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherController.java
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.controllers.tabswitcher;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.controllers.ntp.NewTabPageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+import org.chromium.chrome.test.pagecontroller.utils.UiLocationException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tab Switcher Page Controller.
+ */
+public class TabSwitcherController extends PageController {
+    private static final Pattern PATTERN_NUMBER_OF_OPEN_TABS = Pattern.compile("^(\\d+) .*");
+    private static final IUi2Locator LOCATOR_CLOSE_ALL_TABS =
+            Ui2Locators.withResEntries(R.id.close_all_tabs_button);
+    private static final IUi2Locator LOCATOR_NEW_TAB =
+            Ui2Locators.withResEntries(R.id.tab_switcher_new_tab_button, R.id.new_tab_button);
+    private static final IUi2Locator LOCATOR_TAB_SWITCHER_BUTTON = Ui2Locators.withResEntries(
+            R.id.tab_switcher_button, R.id.tab_switcher_mode_tab_switcher_button);
+    private static final IUi2Locator LOCATOR_MENU = Ui2Locators.withResEntries(R.id.menu_button);
+
+    private static final TabSwitcherController sInstance = new TabSwitcherController();
+
+    static public TabSwitcherController getInstance() {
+        return sInstance;
+    }
+
+    private TabSwitcherController() {}
+
+    public void clickCloseAllTabs() {
+        // Default to  use the close all tabs button.
+        if (mLocatorHelper.isOnScreen(LOCATOR_CLOSE_ALL_TABS)) {
+            mUtils.click(LOCATOR_CLOSE_ALL_TABS);
+        } else {
+            // If it's not found for whatever reason, then do it through the menu.
+            clickMenu().clickCloseAllTabs();
+        }
+    }
+
+    public void clickTabSwitcher() {
+        mUtils.click(LOCATOR_TAB_SWITCHER_BUTTON);
+    }
+
+    public int getNumberOfOpenTabs() {
+        String text = mLocatorHelper.getOneDescription(LOCATOR_TAB_SWITCHER_BUTTON);
+        Matcher matcher = PATTERN_NUMBER_OF_OPEN_TABS.matcher(text);
+        if (matcher.matches()) {
+            return Integer.valueOf(matcher.group(1));
+        } else {
+            throw UiLocationException.newInstance(
+                    "Could not match " + text + " to " + PATTERN_NUMBER_OF_OPEN_TABS);
+        }
+    }
+    public NewTabPageController clickNewTab() {
+        mUtils.click(LOCATOR_NEW_TAB);
+        NewTabPageController inst = NewTabPageController.getInstance();
+        inst.verify();
+        return inst;
+    }
+
+    public TabSwitcherMenuController clickMenu() {
+        mUtils.click(LOCATOR_MENU);
+        TabSwitcherMenuController inst = TabSwitcherMenuController.getInstance();
+        inst.verify();
+        return inst;
+    }
+
+    @Override
+    public boolean isCurrentPageThis() {
+        return mLocatorHelper.isOnScreen(LOCATOR_NEW_TAB);
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherMenuController.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherMenuController.java
new file mode 100644
index 0000000..06db131
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/tabswitcher/TabSwitcherMenuController.java
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.controllers.tabswitcher;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
+import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
+
+/**
+ * Tab Switcher Menu Dialog Page Controller.
+ */
+public class TabSwitcherMenuController extends PageController {
+    private static final IUi2Locator LOCATOR_MENU_BOX =
+            Ui2Locators.withResEntries(R.id.app_menu_list);
+
+    // TODO(aluo): Add resource ids for menus, using text will break when switching languages
+    private static final IUi2Locator LOCATOR_MENU =
+            Ui2Locators.withPath(LOCATOR_MENU_BOX, Ui2Locators.withText("Close all tabs"));
+
+    private static final IUi2Locator LOCATOR_NEW_TAB =
+            Ui2Locators.withResEntriesByIndex(0, R.id.menu_item_text);
+    private static final IUi2Locator LOCATOR_NEW_INCOGNITO_TAB =
+            Ui2Locators.withResEntriesByIndex(1, R.id.menu_item_text);
+    private static final IUi2Locator LOCATOR_CLOSE_ALL_TABS =
+            Ui2Locators.withResEntriesByIndex(2, R.id.menu_item_text);
+    private static final IUi2Locator LOCATOR_SETTINGS =
+            Ui2Locators.withResEntriesByIndex(3, R.id.menu_item_text);
+
+    static private final TabSwitcherMenuController sInstance = new TabSwitcherMenuController();
+    static public TabSwitcherMenuController getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public boolean isCurrentPageThis() {
+        return mLocatorHelper.isOnScreen(LOCATOR_MENU);
+    }
+
+    public void clickNewTab() {
+        mUtils.click(LOCATOR_NEW_TAB);
+    }
+
+    public void clickNewIncognitoTab() {
+        mUtils.click(LOCATOR_NEW_INCOGNITO_TAB);
+    }
+
+    public void clickCloseAllTabs() {
+        mUtils.click(LOCATOR_CLOSE_ALL_TABS);
+    }
+
+    public void clickSettings() {
+        mUtils.click(LOCATOR_SETTINGS);
+    }
+
+    public TabSwitcherController dismiss() {
+        mUtils.clickOutsideOf(LOCATOR_MENU_BOX);
+        TabSwitcherController inst = TabSwitcherController.getInstance();
+        inst.verify();
+        return inst;
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/urlpage/UrlPage.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/urlpage/UrlPage.java
index 977fc46..8d910a95 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/urlpage/UrlPage.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/controllers/urlpage/UrlPage.java
@@ -6,6 +6,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.test.pagecontroller.controllers.PageController;
+import org.chromium.chrome.test.pagecontroller.controllers.tabswitcher.TabSwitcherController;
 import org.chromium.chrome.test.pagecontroller.utils.IUi2Locator;
 import org.chromium.chrome.test.pagecontroller.utils.Ui2Locators;
 import org.chromium.chrome.test.pagecontroller.utils.UiLocatorHelper;
@@ -46,4 +47,11 @@
                 Ui2Locators.withPath(LOCATOR_WEB_VIEW, Ui2Locators.withTextContaining(text));
         return mLocatorHelper.isOnScreen(locator);
     }
+
+    public TabSwitcherController openTabSwitcher() {
+        mUtils.click(LOCATOR_TAB_SWITCHER);
+        TabSwitcherController inst = TabSwitcherController.getInstance();
+        inst.verify();
+        return inst;
+    }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
index 3d56ac86..983603d 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/rules/ChromeUiApplicationTestRule.java
@@ -10,13 +10,9 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.test.pagecontroller.controllers.PageController;
-import org.chromium.chrome.test.pagecontroller.controllers.android.PermissionDialog;
 import org.chromium.chrome.test.pagecontroller.controllers.first_run.DataSaverController;
 import org.chromium.chrome.test.pagecontroller.controllers.first_run.SyncController;
 import org.chromium.chrome.test.pagecontroller.controllers.first_run.TOSController;
-import org.chromium.chrome.test.pagecontroller.controllers.notifications.DownloadNotificationController;
-import org.chromium.chrome.test.pagecontroller.controllers.ntp.ChromeMenu;
-import org.chromium.chrome.test.pagecontroller.controllers.ntp.IncognitoNewTabPageController;
 import org.chromium.chrome.test.pagecontroller.controllers.ntp.NewTabPageController;
 import org.chromium.chrome.test.pagecontroller.utils.UiAutomatorUtils;
 import org.chromium.chrome.test.pagecontroller.utils.UiLocationException;
@@ -47,42 +43,30 @@
         throw UiLocationException.newInstance("Could not detect current Page");
     }
 
-    /**
-     * Detect the page controller from among all page controllers.
-     * When a new page controller is implemented, add it to the list here.
-     * @return                 The detected page controller.
-     * @throws UiLocationError If page can't be determined.
-     */
-    public static PageController detectCurrentPage() {
-        return detectPageAmong(NewTabPageController.getInstance(), SyncController.getInstance(),
-                DataSaverController.getInstance(), TOSController.getInstance(),
-                DownloadNotificationController.getInstance(), PermissionDialog.getInstance(),
-                IncognitoNewTabPageController.getInstance(), ChromeMenu.getInstance());
-    }
-
     /** Launch the chrome application */
     public void launchApplication() {
         UiAutomatorUtils utils = UiAutomatorUtils.getInstance();
         utils.launchApplication(mPackageName);
     }
 
-    /** Navigate to the New Tab Page from somewhere in the application. */
-    public NewTabPageController navigateToNewTabPage() {
-        PageController controller = detectCurrentPage();
+    /**
+     *  Navigate to the New Tab Page after the application starts for the first time, or after
+     *  application data was cleared.
+     *  @return NewTabPageController
+     */
+    public NewTabPageController navigateToNewTabPageOnFirstRun() {
+        PageController controller = detectPageOnFirstRun();
         if (controller instanceof TOSController) {
             ((TOSController) controller).acceptAndContinue();
-            controller = detectCurrentPage();
+            controller = detectPageOnFirstRun();
         }
         if (controller instanceof DataSaverController) {
             ((DataSaverController) controller).clickNext();
-            controller = detectCurrentPage();
+            controller = detectPageOnFirstRun();
         }
         if (controller instanceof SyncController) {
             ((SyncController) controller).clickNoThanks();
-            controller = detectCurrentPage();
-        }
-        if (controller instanceof ChromeMenu) {
-            controller = ((ChromeMenu) controller).dismiss();
+            controller = detectPageOnFirstRun();
         }
         if (controller instanceof NewTabPageController) {
             return (NewTabPageController) controller;
@@ -93,9 +77,9 @@
     }
 
     /** Launch the application and navigate to the New Tab Page */
-    public NewTabPageController launchIntoNewTabPage() {
+    public NewTabPageController launchIntoNewTabPageOnFirstRun() {
         launchApplication();
-        return navigateToNewTabPage();
+        return navigateToNewTabPageOnFirstRun();
     }
 
     public String getApplicationPackage() {
@@ -112,4 +96,16 @@
             mPackageName = InstrumentationRegistry.getTargetContext().getPackageName();
         }
     }
+
+    /**
+     * Detect the page controller from among page controllers that could be displayed on first
+     * launch or after application data was cleared.
+     * Add potential page controllers that could show up before the New Tab Page here.
+     * @return                 The detected page controller.
+     * @throws UiLocationError If page can't be determined.
+     */
+    private static PageController detectPageOnFirstRun() {
+        return detectPageAmong(TOSController.getInstance(), SyncController.getInstance(),
+                DataSaverController.getInstance(), NewTabPageController.getInstance());
+    }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java
index bdbb2bd..15f065f 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/NewTabPageControllerTest.java
@@ -41,7 +41,7 @@
 
     @Before
     public void setUp() {
-        mController = mChromeUiRule.launchIntoNewTabPage();
+        mController = mChromeUiRule.launchIntoNewTabPageOnFirstRun();
     }
 
     @Test
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/TabSwitcherControllerTest.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/TabSwitcherControllerTest.java
new file mode 100644
index 0000000..f964c50e
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/pagecontroller/tests/TabSwitcherControllerTest.java
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.test.pagecontroller.tests;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.chrome.test.pagecontroller.controllers.ntp.NewTabPageController;
+import org.chromium.chrome.test.pagecontroller.controllers.tabswitcher.TabSwitcherController;
+import org.chromium.chrome.test.pagecontroller.controllers.tabswitcher.TabSwitcherMenuController;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiApplicationTestRule;
+import org.chromium.chrome.test.pagecontroller.rules.ChromeUiAutomatorTestRule;
+
+/**
+ * Tests for the TabSwitcherController.
+ */
+@SmallTest
+@RunWith(BaseJUnit4ClassRunner.class)
+public class TabSwitcherControllerTest {
+    public ChromeUiAutomatorTestRule mRule = new ChromeUiAutomatorTestRule();
+
+    public ChromeUiApplicationTestRule mChromeUiRule = new ChromeUiApplicationTestRule();
+
+    @Rule
+    public final TestRule mChain = RuleChain.outerRule(mChromeUiRule).around(mRule);
+
+    private TabSwitcherController mController;
+
+    @Before
+    public void setUp() {
+        mController = mChromeUiRule.launchIntoNewTabPageOnFirstRun().openTabSwitcher();
+    }
+
+    @Test
+    public void testOpenNewTab() {
+        mController.clickNewTab();
+        Assert.assertTrue(NewTabPageController.getInstance().isCurrentPageThis());
+    }
+
+    @Test
+    public void testCloseAllTabs() {
+        mController.clickNewTab().openTabSwitcher().clickNewTab().openTabSwitcher();
+        mController.clickCloseAllTabs();
+        Assert.assertEquals(0, mController.getNumberOfOpenTabs());
+    }
+
+    @Test
+    public void testNumberOfOpenTabs() {
+        int startTabs = mController.getNumberOfOpenTabs();
+        mController.clickNewTab().openTabSwitcher();
+        Assert.assertEquals(startTabs + 1, mController.getNumberOfOpenTabs());
+    }
+
+    @Test
+    public void testClickTabSwitcher() {
+        mController.clickTabSwitcher();
+        Assert.assertTrue(NewTabPageController.getInstance().isCurrentPageThis());
+    }
+
+    @Test
+    public void testOpenMenu() {
+        mController.clickMenu();
+        Assert.assertTrue(TabSwitcherMenuController.getInstance().isCurrentPageThis());
+    }
+}
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index afd3fb0..0386c5a 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -106,10 +106,6 @@
 #include "ui/views/test/test_desktop_screen_x11.h"
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/browser/extension_api_frame_id_map.h"
-#endif
-
 #if defined(TOOLKIT_VIEWS)
 #include "chrome/test/views/accessibility_checker.h"
 #include "ui/views/views_delegate.h"
@@ -316,14 +312,6 @@
   OSCryptMocker::TearDown();
   ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(nullptr);
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // By now, all the WebContents should be destroyed, Ensure that we are not
-  // leaking memory in ExtensionAPIFrameIdMap. crbug.com/817205.
-  EXPECT_EQ(
-      0u,
-      extensions::ExtensionApiFrameIdMap::Get()->GetFrameDataCountForTesting());
-#endif
-
 #if defined(OS_CHROMEOS)
   chromeos::device_sync::DeviceSyncImpl::Factory::SetInstanceForTesting(
       nullptr);
diff --git a/chrome/test/chromedriver/element_commands.cc b/chrome/test/chromedriver/element_commands.cc
index fd52ec7..90f3f83 100644
--- a/chrome/test/chromedriver/element_commands.cc
+++ b/chrome/test/chromedriver/element_commands.cc
@@ -843,15 +843,18 @@
       "({x: document.documentElement.scrollLeft || document.body.scrollLeft,"
       "  y: document.documentElement.scrollTop || document.body.scrollTop,"
       "  height: document.documentElement.clientHeight,"
-      "  width: document.documentElement.clientWidth})",
+      "  width: document.documentElement.clientWidth,"
+      "  device_pixel_ratio: window.devicePixelRatio})",
       &browser_info);
   if (status.IsError())
     return status;
 
-  int scroll_left = browser_info->FindKey("x")->GetInt();
-  int scroll_top = browser_info->FindKey("y")->GetInt();
+  double scroll_left = browser_info->FindKey("x")->GetDouble();
+  double scroll_top = browser_info->FindKey("y")->GetDouble();
   double viewport_height = browser_info->FindKey("height")->GetDouble();
   double viewport_width = browser_info->FindKey("width")->GetDouble();
+  double device_pixel_ratio =
+         browser_info->FindKey("device_pixel_ratio")->GetDouble();
 
   std::unique_ptr<base::DictionaryValue> clip_dict =
       base::DictionaryValue::From(std::move(clip));
@@ -861,14 +864,14 @@
   // element, but its x and y are relative to containing frame. We replace them
   // with the x and y relative to top-level document origin, as expected by
   // CaptureScreenshot.
-  clip_dict->SetInteger("x", location.x + scroll_left);
-  clip_dict->SetInteger("y", location.y + scroll_top);
-  clip_dict->SetDouble("scale", 1.0);
+  clip_dict->SetDouble("x", location.x + scroll_left);
+  clip_dict->SetDouble("y", location.y + scroll_top);
+  clip_dict->SetDouble("scale", 1 / device_pixel_ratio);
   // Crop screenshot by viewport if element is larger than viewport
-  clip_dict->SetInteger(
+  clip_dict->SetDouble(
       "height",
       std::min(viewport_height, clip_dict->FindKey("height")->GetDouble()));
-  clip_dict->SetInteger(
+  clip_dict->SetDouble(
       "width",
       std::min(viewport_width, clip_dict->FindKey("width")->GetDouble()));
   base::DictionaryValue screenshot_params;
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index 86596be..ba6047c 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -96,7 +96,7 @@
 
       <div id="edit-bg" tabindex="0" role="button" hidden>
         <div id="edit-bg-icon"></div>
-        <span id="edit-bg-text"></span>
+        <span id="edit-bg-text">$i18n{customizeButton}</span>
       </div>
 
       <a id="custom-bg-attr"></a>
diff --git a/chrome/test/data/prerender/prefetch_page_multiple_resource_types.html b/chrome/test/data/prerender/prefetch_page_multiple_resource_types.html
index d0a7130..b6ce996 100644
--- a/chrome/test/data/prerender/prefetch_page_multiple_resource_types.html
+++ b/chrome/test/data/prerender/prefetch_page_multiple_resource_types.html
@@ -1,6 +1,7 @@
 <html>
 <body>
   <script src="prefetch.js"></script>
+  <script src="prefetch2.js" async></script>
   <img src="image.png"/>
   <link rel="prefetch" href="prefetch.html">
   <link rel="stylesheet" type="text/css" href="style.css"/>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 54f26bae..6faa4fe 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -82,6 +82,10 @@
     "webui_resource_async_browsertest.js",
   ]
 
+  if (!optimize_webui) {
+    sources += [ "js/webui_resource_module_async_browsertest.js" ]
+  }
+
   extra_js_files = [
     "test_browser_proxy.js",
     "settings/test_password_manager_proxy.js",
diff --git a/chrome/test/data/webui/js/cr_test.js b/chrome/test/data/webui/js/cr_test.js
new file mode 100644
index 0000000..bfc3c2a
--- /dev/null
+++ b/chrome/test/data/webui/js/cr_test.js
@@ -0,0 +1,173 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {addSingletonGetter, addWebUIListener, removeWebUIListener, sendWithPromise, webUIListenerCallback, webUIResponse} from 'chrome://resources/js/cr.m.js';
+import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+
+/** @type {string} Name of the chrome.send() message to be used in tests. */
+const CHROME_SEND_NAME = 'echoMessage';
+
+suite('CrModuleSendWithPromiseTest', function() {
+  let rejectPromises = false;
+
+  function whenChromeSendCalled(name) {
+    return new Promise(function(resolve, reject) {
+      registerMessageCallback(name, null, resolve);
+    });
+  }
+
+  /** @override */
+  setup(function() {
+    // Simulate a WebUI handler that echoes back all parameters passed to it.
+    // Rejects the promise depending on |rejectPromises|.
+    whenChromeSendCalled(CHROME_SEND_NAME).then(function(args) {
+      var callbackId = args[0];
+      webUIResponse.apply(
+          null, [callbackId, !rejectPromises].concat(args.slice(1)));
+    });
+  });
+
+  /** @override */
+  teardown(function() {
+    rejectPromises = false;
+  });
+
+  test('ResponseObject', function() {
+    var expectedResponse = {'foo': 'bar'};
+    return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
+        .then(function(response) {
+          assertEquals(
+              JSON.stringify(expectedResponse), JSON.stringify(response));
+        });
+  });
+
+  test('ResponseArray', function() {
+    var expectedResponse = ['foo', 'bar'];
+    return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
+        .then(function(response) {
+          assertEquals(
+              JSON.stringify(expectedResponse), JSON.stringify(response));
+        });
+  });
+
+  test('ResponsePrimitive', function() {
+    var expectedResponse = 1234;
+    return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
+        .then(function(response) {
+          assertEquals(expectedResponse, response);
+        });
+  });
+
+  test('ResponseVoid', function() {
+    return sendWithPromise(CHROME_SEND_NAME).then(function(response) {
+      assertEquals(undefined, response);
+    });
+  });
+
+  test('Reject', function() {
+    rejectPromises = true;
+    var expectedResponse = 1234;
+    return sendWithPromise(CHROME_SEND_NAME, expectedResponse)
+        .then(
+            function() {
+              assertNotReached('should have rejected promise');
+            },
+            function(error) {
+              assertEquals(expectedResponse, error);
+            });
+  });
+});
+
+suite('CrModuleAddSingletonGetterTest', function() {
+  test('addSingletonGetter', function() {
+    function Foo() {}
+    addSingletonGetter(Foo);
+
+    assertEquals(
+        'function', typeof Foo.getInstance, 'Should add get instance function');
+
+    var x = Foo.getInstance();
+    assertEquals('object', typeof x, 'Should successfully create an object');
+    assertNotEquals(null, x, 'Created object should not be null');
+
+    var y = Foo.getInstance();
+    assertEquals(x, y, 'Should return the same object');
+
+    delete Foo.instance_;
+
+    var z = Foo.getInstance();
+    assertEquals('object', typeof z, 'Should work after clearing for testing');
+    assertNotEquals(null, z, 'Created object should not be null');
+
+    assertNotEquals(
+        x, z, 'Should return a different object after clearing for testing');
+  });
+});
+
+suite('CrModuleWebUIListenersTest', function() {
+  var listener1 = null;
+  var listener2 = null;
+
+  /** @const {string} */
+  var EVENT_NAME = 'my-foo-event';
+
+  teardown(function() {
+    if (listener1) {
+      removeWebUIListener(listener1);
+    }
+    if (listener2) {
+      removeWebUIListener(listener2);
+    }
+  });
+
+  test('removeWebUIListener', function() {
+    listener1 = addWebUIListener(EVENT_NAME, function() {});
+    assertTrue(removeWebUIListener(listener1));
+    assertFalse(removeWebUIListener(listener1));
+    assertFalse(removeWebUIListener({
+      eventName: 'non-existing-event',
+      uid: 12345,
+    }));
+  });
+
+  test('addWebUIListener_ResponseParams', function() {
+    var expectedString = 'foo';
+    var expectedNumber = 123;
+    var expectedArray = [1, 2];
+    var expectedObject = {};
+
+    return new Promise(function(resolve, reject) {
+      listener1 = addWebUIListener(EVENT_NAME, function(s, n, a, o) {
+        assertEquals(expectedString, s);
+        assertEquals(expectedNumber, n);
+        assertEquals(expectedArray, a);
+        assertEquals(expectedObject, o);
+        resolve();
+      });
+      webUIListenerCallback(
+          EVENT_NAME, expectedString, expectedNumber, expectedArray,
+          expectedObject);
+    });
+  });
+
+  test('addWebUIListener_NoResponseParams', function() {
+    return new Promise(function(resolve, reject) {
+      listener1 = addWebUIListener(EVENT_NAME, function() {
+        assertEquals(0, arguments.length);
+        resolve();
+      });
+      webUIListenerCallback(EVENT_NAME);
+    });
+  });
+
+  test('addWebUIListener_MulitpleListeners', function() {
+    var resolver1 = new PromiseResolver();
+    var resolver2 = new PromiseResolver();
+    listener1 = addWebUIListener(EVENT_NAME, resolver1.resolve);
+    listener2 = addWebUIListener(EVENT_NAME, resolver2.resolve);
+    webUIListenerCallback(EVENT_NAME);
+    // Check that both listeners registered are invoked.
+    return Promise.all([resolver1.promise, resolver2.promise]);
+  });
+});
diff --git a/chrome/test/data/webui/js/i18n_behavior_test.js b/chrome/test/data/webui/js/i18n_behavior_test.js
new file mode 100644
index 0000000..a7c73bbe
--- /dev/null
+++ b/chrome/test/data/webui/js/i18n_behavior_test.js
@@ -0,0 +1,65 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+
+suite('I18nBehaviorModuleTest', function() {
+  const allowedByDefault = '<a href="https://google.com">Google!</a>';
+  const text = 'I\'m just text, nobody should have a problem with me!';
+  const nonBreakingSpace = 'A\u00a0B\u00a0C';  // \u00a0 is a unicode nbsp.
+
+  suiteSetup(function() {
+    window.loadTimeData.data = {
+      'allowedByDefault': allowedByDefault,
+      'customAttr': '<a is="action-link">Take action!</a>',
+      'customTag': '<x-foo>I\'m an X, foo!</x-foo>',
+      'javascriptHref': '<a href="javascript:alert(1)">teh hax</a>',
+      'script': '<script>alert(/xss/)</scr' +
+          'ipt>',
+      'text': text,
+      'nonBreakingSpace': nonBreakingSpace,
+    };
+  });
+
+  test('i18n', function() {
+    assertEquals(text, I18nBehavior.i18n('text'));
+    assertEquals(nonBreakingSpace, I18nBehavior.i18n('nonBreakingSpace'));
+
+    assertThrows(function() {
+      I18nBehavior.i18n('customAttr');
+    });
+    assertThrows(function() {
+      I18nBehavior.i18n('customTag');
+    });
+    assertThrows(function() {
+      I18nBehavior.i18n('javascriptHref');
+    });
+    assertThrows(function() {
+      I18nBehavior.i18n('script');
+    });
+  });
+
+  test('i18n advanced', function() {
+    assertEquals(
+        allowedByDefault, I18nBehavior.i18nAdvanced('allowedByDefault'));
+    I18nBehavior.i18nAdvanced('customAttr', {
+      attrs: {
+        is: function(el, val) {
+          return el.tagName == 'A' && val == 'action-link';
+        },
+      },
+    });
+    I18nBehavior.i18nAdvanced('customTag', {tags: ['X-FOO']});
+  });
+
+  test('i18n dynamic', function() {
+    var locale = 'en';
+    assertEquals(text, I18nBehavior.i18nDynamic(locale, 'text'));
+  });
+
+  test('i18n exists', function() {
+    assertTrue(I18nBehavior.i18nExists('text'));
+    assertFalse(I18nBehavior.i18nExists('missingText'));
+  });
+});
diff --git a/chrome/test/data/webui/js/load_time_data_test.js b/chrome/test/data/webui/js/load_time_data_test.js
new file mode 100644
index 0000000..15ae67a8
--- /dev/null
+++ b/chrome/test/data/webui/js/load_time_data_test.js
@@ -0,0 +1,105 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/js/load_time_data.m.js';
+
+suite('LoadTimeDataModuleTest', function() {
+  const loadTimeData = window.loadTimeData;
+
+  test('sanitizeInnerHtml', function() {
+    // A few tests to see that that data is being passed through. The
+    // sanitizeInnerHtml() function calls into parseHtmlSubset() which has its
+    // own tests (that don't need to be repeated here).
+    assertEquals(
+        '<a href="chrome://foo"></a>',
+        loadTimeData.sanitizeInnerHtml('<a href="chrome://foo"></a>'));
+    assertThrows(() => {
+      loadTimeData.sanitizeInnerHtml('<div></div>');
+    }, 'DIV is not supported');
+    assertEquals(
+        '<div></div>',
+        loadTimeData.sanitizeInnerHtml('<div></div>', {tags: ['div']}));
+  });
+
+  test('getStringPieces', function() {
+    const assertSubstitutedPieces = function(expected, var_args) {
+      var var_args = Array.prototype.slice.call(arguments, 1);
+      var pieces =
+          loadTimeData.getSubstitutedStringPieces.apply(loadTimeData, var_args);
+      assertDeepEquals(expected, pieces);
+
+      // Ensure output matches getStringF.
+      assertEquals(
+          loadTimeData.substituteString.apply(loadTimeData, var_args),
+          pieces.map(p => p.value).join(''));
+    };
+
+    assertSubstitutedPieces([{value: 'paper', arg: null}], 'paper');
+    assertSubstitutedPieces([{value: 'paper', arg: '$1'}], '$1', 'paper');
+
+    assertSubstitutedPieces(
+        [
+          {value: 'i think ', arg: null},
+          {value: 'paper mario', arg: '$1'},
+          {value: ' is a good game', arg: null},
+        ],
+        'i think $1 is a good game', 'paper mario');
+
+    assertSubstitutedPieces(
+        [
+          {value: 'paper mario', arg: '$1'},
+          {value: ' costs $', arg: null},
+          {value: '60', arg: '$2'},
+        ],
+        '$1 costs $$$2', 'paper mario', '60');
+
+    assertSubstitutedPieces(
+        [
+          {value: 'paper mario', arg: '$1'},
+          {value: ' costs $60', arg: null},
+        ],
+        '$1 costs $$60', 'paper mario');
+
+    assertSubstitutedPieces(
+        [
+          {value: 'paper mario', arg: '$1'},
+          {value: ' costs\n$60 ', arg: null},
+          {value: 'today', arg: '$2'},
+        ],
+        '$1 costs\n$$60 $2', 'paper mario', 'today');
+
+    assertSubstitutedPieces(
+        [
+          {value: '$$', arg: null},
+          {value: '1', arg: '$1'},
+          {value: '2', arg: '$2'},
+          {value: '1', arg: '$1'},
+          {value: '$$2', arg: null},
+          {value: '2', arg: '$2'},
+          {value: '$', arg: null},
+          {value: '1', arg: '$1'},
+          {value: '$', arg: null},
+        ],
+        '$$$$$1$2$1$$$$2$2$$$1$$', '1', '2');
+  });
+
+  test('unescapedDollarSign', function() {
+    /** @param {string} label */
+    const assertSubstitutionThrows = function(label) {
+      assertThrows(() => {
+        loadTimeData.getSubstitutedStringPieces(label);
+      }, 'Assertion failed: Unescaped $ found in localized string.');
+
+      assertThrows(() => {
+        loadTimeData.substituteString(label);
+      }, 'Assertion failed: Unescaped $ found in localized string.');
+    };
+
+    assertSubstitutionThrows('$');
+    assertSubstitutionThrows('$1$$$a2');
+    assertSubstitutionThrows('$$$');
+    assertSubstitutionThrows('a$');
+    assertSubstitutionThrows('a$\n');
+  });
+});
diff --git a/chrome/test/data/webui/js/parse_html_subset_test.js b/chrome/test/data/webui/js/parse_html_subset_test.js
new file mode 100644
index 0000000..97538e3
--- /dev/null
+++ b/chrome/test/data/webui/js/parse_html_subset_test.js
@@ -0,0 +1,121 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {parseHtmlSubset} from 'chrome://resources/js/parse_html_subset.m.js';
+
+suite('ParseHtmlSubsetModuleTest', function() {
+  function parseAndAssertThrows() {
+    var args = arguments;
+    assertThrows(function() {
+      parseHtmlSubset.apply(null, args);
+    });
+  }
+
+  test('text', function() {
+    parseHtmlSubset('');
+    parseHtmlSubset('abc');
+    parseHtmlSubset('&nbsp;');
+  });
+
+  test('supported tags', function() {
+    parseHtmlSubset('<b>bold</b>');
+    parseHtmlSubset('Some <b>bold</b> text');
+    parseHtmlSubset('Some <strong>strong</strong> text');
+    parseHtmlSubset('<B>bold</B>');
+    parseHtmlSubset('Some <B>bold</B> text');
+    parseHtmlSubset('Some <STRONG>strong</STRONG> text');
+  });
+
+  test('invalid tags', function() {
+    parseAndAssertThrows('<unknown_tag>x</unknown_tag>');
+    parseAndAssertThrows('<img>');
+    parseAndAssertThrows(
+        '<script>alert(1)<' +
+        '/script>');
+  });
+
+  test('invalid attributes', function() {
+    parseAndAssertThrows('<b onclick="alert(1)">x</b>');
+    parseAndAssertThrows('<b style="color:red">x</b>');
+    parseAndAssertThrows('<b foo>x</b>');
+    parseAndAssertThrows('<b foo=bar></b>');
+  });
+
+  test('valid anchors', function() {
+    parseHtmlSubset('<a href="https://google.com">Google</a>');
+    parseHtmlSubset('<a href="chrome://settings">Google</a>');
+  });
+
+  test('invalid anchor hrefs', function() {
+    parseAndAssertThrows('<a href="http://google.com">Google</a>');
+    parseAndAssertThrows('<a href="ftp://google.com">Google</a>');
+    parseAndAssertThrows('<a href="http/google.com">Google</a>');
+    parseAndAssertThrows('<a href="javascript:alert(1)">Google</a>');
+    parseAndAssertThrows(
+        '<a href="chrome-extension://whurblegarble">Google</a>');
+  });
+
+  test('invalid anchor attributes', function() {
+    parseAndAssertThrows('<a name=foo>Google</a>');
+    parseAndAssertThrows(
+        '<a onclick="alert(1)" href="https://google.com">Google</a>');
+    parseAndAssertThrows(
+        '<a foo="bar(1)" href="https://google.com">Google</a>');
+  });
+
+  test('anchor target', function() {
+    var df = parseHtmlSubset(
+        '<a href="https://google.com" target="_blank">Google</a>');
+    assertEquals('_blank', df.firstChild.target);
+  });
+
+  test('invalid target', function() {
+    parseAndAssertThrows('<form target="_evil">', ['form']);
+    parseAndAssertThrows('<iframe target="_evil">', ['iframe']);
+    parseAndAssertThrows(
+        '<a href="https://google.com" target="foo">Google</a>');
+  });
+
+  test('custom tags', function() {
+    parseHtmlSubset('yo <I>ho</i><bR>yo <EM>ho</em>', ['i', 'EM', 'Br']);
+  });
+
+  test('invalid custom tags', function() {
+    parseAndAssertThrows(
+        'a pirate\'s<script>lifeForMe();<' +
+            '/script>',
+        ['br']);
+  });
+
+  test('custom attributes', function() {
+    const returnsTruthy = function(node, value) {
+      assertEquals('A', node.tagName);
+      assertEquals('fancy', value);
+      return true;
+    };
+    parseHtmlSubset(
+        '<a class="fancy">I\'m fancy!</a>', null, {class: returnsTruthy});
+  });
+
+  test('invalid custom attributes', function() {
+    const returnsFalsey = function() {
+      return false;
+    };
+    parseAndAssertThrows(
+        '<a class="fancy">I\'m fancy!</a>', null, {class: returnsFalsey});
+    parseAndAssertThrows('<a class="fancy">I\'m fancy!</a>');
+  });
+
+  test('on error async', function(done) {
+    window.called = false;
+
+    parseAndAssertThrows('<img onerror="window.called = true" src="_.png">');
+    parseAndAssertThrows('<img src="_.png" onerror="window.called = true">');
+
+    window.setTimeout(function() {
+      assertFalse(window.called);
+      done();
+    });
+  });
+});
diff --git a/chrome/test/data/webui/js/promise_resolver_test.js b/chrome/test/data/webui/js/promise_resolver_test.js
new file mode 100644
index 0000000..52e447e
--- /dev/null
+++ b/chrome/test/data/webui/js/promise_resolver_test.js
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
+
+suite('PromiseResolverModuleTest', function() {
+  test('members read only', function() {
+    const resolver = new PromiseResolver;
+    assertThrows(function() {
+      resolver.promise = new Promise;
+    });
+    assertThrows(function() {
+      resolver.resolve = function() {};
+    });
+    assertThrows(function() {
+      resolver.reject = function() {};
+    });
+  });
+
+  test('resolves', function(done) {
+    const resolver = new PromiseResolver;
+    resolver.promise.then(done);
+    resolver.resolve();
+  });
+
+  test('rejects', function(done) {
+    const resolver = new PromiseResolver;
+    resolver.promise.catch(done);
+    resolver.reject();
+  });
+
+  test('is fulfilled', function() {
+    const resolver1 = new PromiseResolver;
+    assertFalse(resolver1.isFulfilled);
+    resolver1.resolve();
+    assertTrue(resolver1.isFulfilled);
+
+    const resolver2 = new PromiseResolver;
+    assertFalse(resolver2.isFulfilled);
+    resolver2.resolve(true);
+    assertTrue(resolver2.isFulfilled);
+
+    const resolver3 = new PromiseResolver;
+    assertFalse(resolver3.isFulfilled);
+    resolver3.reject(new Error);
+    assertTrue(resolver3.isFulfilled);
+  });
+});
diff --git a/chrome/test/data/webui/js/util_test.js b/chrome/test/data/webui/js/util_test.js
new file mode 100644
index 0000000..af20ec2
--- /dev/null
+++ b/chrome/test/data/webui/js/util_test.js
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {$, quoteString} from 'chrome://resources/js/util.m.js';
+
+suite('UtilModuleTest', function() {
+  test('quote string', function() {
+    // Basic cases.
+    assertEquals('\"test\"', quoteString('"test"'));
+    assertEquals('\\!\\?', quoteString('!?'));
+    assertEquals(
+        '\\(\\._\\.\\) \\( \\:l \\) \\(\\.-\\.\\)',
+        quoteString('(._.) ( :l ) (.-.)'));
+
+    // Using the output as a regex.
+    var re = new RegExp(quoteString('"hello"'), 'gim');
+    var match = re.exec('She said "Hello" loudly');
+    assertEquals(9, match.index);
+
+    re = new RegExp(quoteString('Hello, .*'), 'gim');
+    match = re.exec('Hello, world');
+    assertEquals(null, match);
+  });
+
+  test('click handler', function() {
+    document.body.innerHTML = `
+      <a id="file" href="file:///path/to/file">File</a>
+      <a id="chrome" href="about:chrome">Chrome</a>
+      <a href="about:blank"><b id="blank">Click me</b></a>
+    `;
+
+    var clickArgs = null;
+    var oldSend = chrome.send;
+    chrome.send = function(message, args) {
+      assertEquals('navigateToUrl', message);
+      clickArgs = args;
+    };
+    $('file').click();
+    assertEquals('file:///path/to/file', clickArgs[0]);
+    $('chrome').click();
+    assertEquals('about:chrome', clickArgs[0]);
+    $('blank').click();
+    assertEquals('about:blank', clickArgs[0]);
+    chrome.send = oldSend;
+  });
+});
diff --git a/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js b/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js
new file mode 100644
index 0000000..124a1fc
--- /dev/null
+++ b/chrome/test/data/webui/js/webui_resource_module_async_browsertest.js
@@ -0,0 +1,112 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview JS tests for various chrome://resources JS modules.
+ */
+
+/** Test fixture for testing shared JS module resources. */
+var WebUIResourceModuleAsyncTest = class extends testing.Test {
+  /** @override */
+  get browsePreload() {
+    return DUMMY_URL;
+  }
+
+  /** @override */
+  get isAsync() {
+    return true;
+  }
+
+  /** @override */
+  get runAccessibilityChecks() {
+    return false;
+  }
+
+  /** @override */
+  get webuiHost() {
+    return 'dummyurl';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return [
+      '//third_party/mocha/mocha.js',
+      '//chrome/test/data/webui/mocha_adapter.js',
+    ];
+  }
+};
+
+var CrModuleTest = class extends WebUIResourceModuleAsyncTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test?module=js/cr_test.js';
+  }
+};
+
+TEST_F('CrModuleTest', 'AddSingletonGetter', function() {
+  mocha.fgrep('CrModuleAddSingletonGetterTest').run();
+});
+
+TEST_F('CrModuleTest', 'SendWithPromise', function() {
+  mocha.fgrep('CrModuleSendWithPromiseTest').run();
+});
+
+TEST_F('CrModuleTest', 'WebUIListeners', function() {
+  mocha.fgrep('CrModuleWebUIListenersTest').run();
+});
+
+var PromiseResolverModuleTest = class extends WebUIResourceModuleAsyncTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test?module=js/promise_resolver_test.js';
+  }
+};
+
+TEST_F('PromiseResolverModuleTest', 'All', function() {
+  mocha.run();
+});
+
+var ParseHtmlSubsetModuleTest = class extends WebUIResourceModuleAsyncTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test?module=js/parse_html_subset_test.js';
+  }
+};
+
+TEST_F('ParseHtmlSubsetModuleTest', 'All', function() {
+  mocha.run();
+});
+
+var UtilModuleTest = class extends WebUIResourceModuleAsyncTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test?module=js/util_test.js';
+  }
+};
+
+TEST_F('UtilModuleTest', 'All', function() {
+  mocha.run();
+});
+
+var LoadTimeDataModuleTest = class extends WebUIResourceModuleAsyncTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test?module=js/load_time_data_test.js';
+  }
+};
+
+TEST_F('LoadTimeDataModuleTest', 'All', function() {
+  mocha.run();
+});
+
+var I18nBehaviorModuleTest = class extends WebUIResourceModuleAsyncTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://test?module=js/i18n_behavior_test.js';
+  }
+};
+
+TEST_F('I18nBehaviorModuleTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chromecast/common/extensions_api/_api_features.json b/chromecast/common/extensions_api/_api_features.json
index 371548d..7cf1009 100644
--- a/chromecast/common/extensions_api/_api_features.json
+++ b/chromecast/common/extensions_api/_api_features.json
@@ -27,7 +27,8 @@
     "contexts": ["blessed_extension"]
   },
   "extension.getURL": {
-    "contexts": ["blessed_extension", "unblessed_extension", "content_script", "extension_service_worker"]
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "disallow_for_service_workers": false
   },
   "extension.getViews": [
     {
@@ -97,7 +98,8 @@
   "tabs": [{
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   }, {
     "channel": "stable",
     "contexts": ["webui"],
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc
index 9c78614f..802e3d5 100644
--- a/chromeos/constants/chromeos_switches.cc
+++ b/chromeos/constants/chromeos_switches.cc
@@ -345,6 +345,11 @@
 const char kFakeDriveFsLauncherSocketPath[] =
     "fake-drivefs-launcher-socket-path";
 
+// Forces Chrome to use CertVerifyProcBuiltin for verification of server
+// certificates, ignoring the status of
+// net::features::kCertVerifierBuiltinFeature.
+const char kForceCertVerifierBuiltin[] = "force-cert-verifier-builtin";
+
 // Passed to Chrome the first time that it's run after the system boots.
 // Not passed on restart after sign out.
 const char kFirstExecAfterBoot[] = "first-exec-after-boot";
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h
index 488cf1ba..7bd9cecf 100644
--- a/chromeos/constants/chromeos_switches.h
+++ b/chromeos/constants/chromeos_switches.h
@@ -136,6 +136,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kFakeDriveFsLauncherSocketPath[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kForceCertVerifierBuiltin[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kForceDevToolsAvailable[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kForceFirstRunUI[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index fdc393d2..2bfa2ce 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -126,6 +126,7 @@
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
     "//chromeos/services/device_sync/public/mojom",
+    "//components/signin/public/identity_manager",
     "//services/network/public/cpp",
     "//services/service_manager/public/cpp",
   ]
@@ -141,7 +142,6 @@
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
     "//components/gcm_driver",
-    "//components/signin/public/identity_manager",
     "//net",
     "//services/preferences/public/cpp",
     "//services/service_manager/public/cpp",
@@ -167,6 +167,8 @@
     "fake_cryptauth_ecies_encryptor.h",
     "fake_cryptauth_enrollment_manager.cc",
     "fake_cryptauth_enrollment_manager.h",
+    "fake_cryptauth_feature_status_getter.cc",
+    "fake_cryptauth_feature_status_getter.h",
     "fake_cryptauth_gcm_manager.cc",
     "fake_cryptauth_gcm_manager.h",
     "fake_cryptauth_group_private_key_sharer.cc",
diff --git a/chromeos/services/device_sync/DEPS b/chromeos/services/device_sync/DEPS
index d9f97cb..c202c86 100644
--- a/chromeos/services/device_sync/DEPS
+++ b/chromeos/services/device_sync/DEPS
@@ -2,7 +2,6 @@
   "+components/cryptauth",
   "+components/gcm_driver",
   "+components/proximity_auth/logging",
-  "+components/signin/core/browser",
   "+components/signin/public",
   "+components/version_info",
   "+google_apis/gaia",
diff --git a/chromeos/services/device_sync/cryptauth_client_impl.cc b/chromeos/services/device_sync/cryptauth_client_impl.cc
index 100d2fdb..93ac755 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl.cc
+++ b/chromeos/services/device_sync/cryptauth_client_impl.cc
@@ -14,6 +14,7 @@
 #include "chromeos/services/device_sync/proto/cryptauth_enrollment.pb.h"
 #include "chromeos/services/device_sync/proto/cryptauth_proto_to_query_parameters_util.h"
 #include "chromeos/services/device_sync/switches.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/chromeos/services/device_sync/cryptauth_client_impl.h b/chromeos/services/device_sync/cryptauth_client_impl.h
index 0b672ce4..9d804d9ae 100644
--- a/chromeos/services/device_sync/cryptauth_client_impl.h
+++ b/chromeos/services/device_sync/cryptauth_client_impl.h
@@ -15,10 +15,10 @@
 #include "chromeos/services/device_sync/cryptauth_api_call_flow.h"
 #include "chromeos/services/device_sync/cryptauth_client.h"
 #include "chromeos/services/device_sync/proto/cryptauth_api.pb.h"
-#include "components/signin/public/identity_manager/access_token_info.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 
 namespace signin {
+struct AccessTokenInfo;
 class IdentityManager;
 class PrimaryAccountAccessTokenFetcher;
 }  // namespace signin
diff --git a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc
new file mode 100644
index 0000000..bca3d6949
--- /dev/null
+++ b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+FakeCryptAuthFeatureStatusGetter::FakeCryptAuthFeatureStatusGetter() = default;
+
+FakeCryptAuthFeatureStatusGetter::~FakeCryptAuthFeatureStatusGetter() = default;
+
+void FakeCryptAuthFeatureStatusGetter::FinishAttempt(
+    const IdToFeatureStatusMap& id_to_feature_status_map,
+    const CryptAuthDeviceSyncResult::ResultCode& device_sync_result_code) {
+  DCHECK(request_context_);
+  DCHECK(device_ids_);
+
+  OnAttemptFinished(id_to_feature_status_map, device_sync_result_code);
+}
+
+void FakeCryptAuthFeatureStatusGetter::OnAttemptStarted(
+    const cryptauthv2::RequestContext& request_context,
+    const base::flat_set<std::string>& device_ids) {
+  request_context_ = request_context;
+  device_ids_ = device_ids;
+}
+
+FakeCryptAuthFeatureStatusGetterFactory::
+    FakeCryptAuthFeatureStatusGetterFactory() = default;
+
+FakeCryptAuthFeatureStatusGetterFactory::
+    ~FakeCryptAuthFeatureStatusGetterFactory() = default;
+
+std::unique_ptr<CryptAuthFeatureStatusGetter>
+FakeCryptAuthFeatureStatusGetterFactory::BuildInstance(
+    CryptAuthClientFactory* client_factory,
+    std::unique_ptr<base::OneShotTimer> timer) {
+  last_client_factory_ = client_factory;
+
+  auto instance = std::make_unique<FakeCryptAuthFeatureStatusGetter>();
+  instances_.push_back(instance.get());
+
+  return instance;
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
new file mode 100644
index 0000000..34f94ae
--- /dev/null
+++ b/chromeos/services/device_sync/fake_cryptauth_feature_status_getter.h
@@ -0,0 +1,93 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_FEATURE_STATUS_GETTER_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_FEATURE_STATUS_GETTER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/timer/timer.h"
+#include "chromeos/services/device_sync/cryptauth_device_sync_result.h"
+#include "chromeos/services/device_sync/cryptauth_feature_status_getter.h"
+#include "chromeos/services/device_sync/cryptauth_feature_status_getter_impl.h"
+#include "chromeos/services/device_sync/proto/cryptauth_devicesync.pb.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+class CryptAuthClientFactory;
+
+class FakeCryptAuthFeatureStatusGetter : public CryptAuthFeatureStatusGetter {
+ public:
+  FakeCryptAuthFeatureStatusGetter();
+  ~FakeCryptAuthFeatureStatusGetter() override;
+
+  // The RequestContext passed to GetFeatureStatuses(). Returns null if
+  // GetFeatureStatuses() has not been called yet.
+  const base::Optional<cryptauthv2::RequestContext>& request_context() const {
+    return request_context_;
+  }
+
+  // The device IDs passed to GetFeatureStatuses(). Returns null if
+  // GetFeatureStatuses() has not been called yet.
+  const base::Optional<base::flat_set<std::string>>& device_ids() const {
+    return device_ids_;
+  }
+
+  // Calls OnAttemptFinished() with the same input parameters.
+  void FinishAttempt(
+      const IdToFeatureStatusMap& id_to_feature_status_map,
+      const CryptAuthDeviceSyncResult::ResultCode& device_sync_result_code);
+
+ private:
+  // CryptAuthFeatureStatusGetter:
+  void OnAttemptStarted(const cryptauthv2::RequestContext& request_context,
+                        const base::flat_set<std::string>& device_ids) override;
+
+  base::Optional<cryptauthv2::RequestContext> request_context_;
+  base::Optional<base::flat_set<std::string>> device_ids_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCryptAuthFeatureStatusGetter);
+};
+
+class FakeCryptAuthFeatureStatusGetterFactory
+    : public CryptAuthFeatureStatusGetterImpl::Factory {
+ public:
+  FakeCryptAuthFeatureStatusGetterFactory();
+  ~FakeCryptAuthFeatureStatusGetterFactory() override;
+
+  // Returns a vector of all FakeCryptAuthFeatureStatusGetter instances created
+  // by BuildInstance().
+  const std::vector<FakeCryptAuthFeatureStatusGetter*>& instances() const {
+    return instances_;
+  }
+
+  // Returns the most recent CryptAuthClientFactory input into BuildInstance().
+  const CryptAuthClientFactory* last_client_factory() const {
+    return last_client_factory_;
+  }
+
+ private:
+  // CryptAuthFeatureStatusGetterImpl::Factory:
+  std::unique_ptr<CryptAuthFeatureStatusGetter> BuildInstance(
+      CryptAuthClientFactory* client_factory,
+      std::unique_ptr<base::OneShotTimer> timer = nullptr) override;
+
+  std::vector<FakeCryptAuthFeatureStatusGetter*> instances_;
+  CryptAuthClientFactory* last_client_factory_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCryptAuthFeatureStatusGetterFactory);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  //  CHROMEOS_SERVICES_DEVICE_SYNC_FAKE_CRYPTAUTH_FEATURE_STATUS_GETTER_H_
diff --git a/components/chromeos_camera/BUILD.gn b/components/chromeos_camera/BUILD.gn
index ca6a83c..20c5840 100644
--- a/components/chromeos_camera/BUILD.gn
+++ b/components/chromeos_camera/BUILD.gn
@@ -207,6 +207,11 @@
     deps += [
       "//media/gpu/vaapi",
       "//media/gpu/vaapi:jpeg_decoder_unit_test",
+      # TODO(crbug.com/986074): we should move this to its own executable, but
+      # this would imply creating a new Tast wrapper in Chrome OS. So, for
+      # convenience, we make the WebP tests run with the
+      # jpeg_decode_accelerator_unittest binary.
+      "//media/gpu/vaapi:webp_decoder_unit_test",
     ]
 
     data += [ "//media/test/data/pixel-1280x720.jpg" ]
diff --git a/components/flags_ui/resources/flags.js b/components/flags_ui/resources/flags.js
index d2dbc2d..e44879f 100644
--- a/components/flags_ui/resources/flags.js
+++ b/components/flags_ui/resources/flags.js
@@ -128,26 +128,20 @@
 }
 
 /**
- * Asks the C++ FlagsDOMHandler to get details about the available experimental
- * features and return detailed data about the configuration. The
- * FlagsDOMHandler should reply to returnFlagsExperiments() (below).
+ * Gets details and configuration about the available features. The
+ * |returnExperimentalFeatures()| will be called with reply.
  */
 function requestExperimentalFeaturesData() {
   chrome.send('requestExperimentalFeatures');
 }
 
-/**
- * Asks the C++ FlagsDOMHandler to restart the browser (restoring tabs).
- */
+/** Restart browser and restore tabs. */
 function restartBrowser() {
   chrome.send('restartBrowser');
 }
 
-/**
- * Reset all flags to their default values and refresh the UI.
- */
+/** Reset all flags to their default values and refresh the UI. */
 function resetAllFlags() {
-  // Asks the C++ FlagsDOMHandler to reset all flags to default values.
   chrome.send('resetAllFlags');
   showRestartToast(true);
   requestExperimentalFeaturesData();
@@ -248,7 +242,6 @@
  * @param {boolean} enable Whether to enable or disable the experiment.
  */
 function handleEnableExperimentalFeature(node, enable) {
-  // Tell the C++ FlagsDOMHandler to enable/disable the experiment.
   chrome.send('enableExperimentalFeature', [String(node.internal_name),
                                             String(enable)]);
   experimentChangesUiUpdates(node, enable ? 1 : 0);
@@ -266,7 +259,6 @@
  * @param {number} index The index of the option that was selected.
  */
 function handleSelectExperimentalFeatureChoice(node, index) {
-  // Tell the C++ FlagsDOMHandler to enable the selected choice.
   chrome.send('enableExperimentalFeature',
               [String(node.internal_name) + '@' + index, 'true']);
   experimentChangesUiUpdates(node, index);
diff --git a/components/safe_browsing/proto/webprotect.proto b/components/safe_browsing/proto/webprotect.proto
index fdde7bf..d0c2e6f5 100644
--- a/components/safe_browsing/proto/webprotect.proto
+++ b/components/safe_browsing/proto/webprotect.proto
@@ -61,4 +61,8 @@
 
   // DLP scan specific response info.
   optional DlpDeepScanningClientResponse dlp_scan_response = 2;
+
+  // Token used to correlate requests and responses. This is different than the
+  // FCM token, in that it is unique for each request.
+  optional string token = 3;
 }
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.cc b/components/send_tab_to_self/send_tab_to_self_bridge.cc
index 3b4f938b..e10dfefe 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -553,16 +553,17 @@
   if (base::FeatureList::IsEnabled(kSendTabToSelfBroadcast)) {
     new_local_entries = new_entries;
   } else {
-    // Only pass along entries that are targeted at this device, and not
-    // dismissed or opened.
+    // Only pass along entries that are not dismissed or opened, and are
+    // targeted at this device, which is determined by comparing the cache guid
+    // associated with the entry to each device's local list of recently used
+    // cache_guids
     DCHECK(!change_processor()->TrackedCacheGuid().empty());
     for (const SendTabToSelfEntry* entry : new_entries) {
-      if (entry->GetTargetDeviceSyncCacheGuid() ==
-              change_processor()->TrackedCacheGuid() &&
+      if (device_info_tracker_->IsRecentLocalCacheGuid(
+              entry->GetTargetDeviceSyncCacheGuid()) &&
           !entry->GetNotificationDismissed() && !entry->IsOpened()) {
         new_local_entries.push_back(entry);
         LogLocalDeviceNotified(UMANotifyLocalDevice::LOCAL);
-
       } else {
         LogLocalDeviceNotified(UMANotifyLocalDevice::REMOTE);
       }
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
index 758b2da..3f3ffee 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
@@ -94,6 +94,12 @@
       : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
     scoped_feature_list_.InitAndEnableFeature(kSendTabToSelfShowSendingUI);
     SetLocalDeviceCacheGuid(kLocalDeviceCacheGuid);
+    local_device_ = std::make_unique<syncer::DeviceInfo>(
+        kLocalDeviceCacheGuid, "device", "72", "agent",
+        sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+        clock()->Now() - base::TimeDelta::FromDays(1),
+        /*send_tab_to_self_receiving_enabled=*/true);
+    AddTestDevice(local_device_.get());
   }
 
   // Initialized the bridge based on the current local device and store. Can
@@ -203,6 +209,8 @@
 
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  std::unique_ptr<syncer::DeviceInfo> local_device_;
+
   DISALLOW_COPY_AND_ASSIGN(SendTabToSelfBridgeTest);
 };
 
@@ -253,6 +261,7 @@
 
 TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneAdd) {
   InitializeBridge();
+
   SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
                            AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
                            kLocalDeviceCacheGuid);
@@ -273,6 +282,7 @@
 // Tests that the send tab to self entry is correctly removed.
 TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneDeletion) {
   InitializeBridge();
+
   SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
                            AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
                            kLocalDeviceCacheGuid);
@@ -565,17 +575,17 @@
       /*enabled_features=*/{kSendTabToSelfShowSendingUI},
       /*disabled_features=*/{kSendTabToSelfBroadcast});
 
+  const std::string kRemoteGuid = "RemoteDevice";
   InitializeBridge();
-  SetLocalDeviceCacheGuid("Device1");
 
   // Add on entry targeting this device and another targeting another device.
   syncer::EntityChangeList remote_input;
   SendTabToSelfEntry entry1("guid1", GURL("http://www.example.com/"), "title",
                             AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
-                            "Device1");
+                            kLocalDeviceCacheGuid);
   SendTabToSelfEntry entry2("guid2", GURL("http://www.example.com/"), "title",
                             AdvanceAndGetTime(), AdvanceAndGetTime(), "device",
-                            "Device2");
+                            kRemoteGuid);
   remote_input.push_back(
       syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry1)));
   remote_input.push_back(
diff --git a/components/session_manager/core/session_manager.cc b/components/session_manager/core/session_manager.cc
index bb94497..ed4a656 100644
--- a/components/session_manager/core/session_manager.cc
+++ b/components/session_manager/core/session_manager.cc
@@ -24,6 +24,9 @@
 SessionManager::~SessionManager() {
   DCHECK_EQ(instance, this);
   SessionManager::SetInstance(nullptr);
+
+  for (auto& observer : observers_)
+    observer.OnSessionManagerDestroyed();
 }
 
 // static
diff --git a/components/session_manager/core/session_manager_observer.h b/components/session_manager/core/session_manager_observer.h
index e3748ca..98317fa 100644
--- a/components/session_manager/core/session_manager_observer.h
+++ b/components/session_manager/core/session_manager_observer.h
@@ -16,6 +16,9 @@
 //     http://crbug.com/657149.
 class SessionManagerObserver : public base::CheckedObserver {
  public:
+  // Inovked when session manager is destroyed.
+  virtual void OnSessionManagerDestroyed() {}
+
   // Invoked when session state is changed.
   virtual void OnSessionStateChanged() {}
 
diff --git a/components/signin/core/browser/signin_header_helper.h b/components/signin/core/browser/signin_header_helper.h
index 265698b..ac718de 100644
--- a/components/signin/core/browser/signin_header_helper.h
+++ b/components/signin/core/browser/signin_header_helper.h
@@ -42,7 +42,7 @@
 // perform.
 // A Java counterpart will be generated for this enum.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.signin
-enum GAIAServiceType {
+enum GAIAServiceType : int {
   GAIA_SERVICE_TYPE_NONE = 0,    // No Gaia response header.
   GAIA_SERVICE_TYPE_SIGNOUT,     // Logout all existing sessions.
   GAIA_SERVICE_TYPE_INCOGNITO,   // Open an incognito tab.
diff --git a/components/sync/driver/resources/BUILD.gn b/components/sync/driver/resources/BUILD.gn
new file mode 100644
index 0000000..137833b6
--- /dev/null
+++ b/components/sync/driver/resources/BUILD.gn
@@ -0,0 +1,106 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    #TODO(crbug.com/986001): Fix compilation errors and enable all targets.
+
+    #":about",
+    ":chrome_sync",
+
+    #":data",
+    ":events",
+    ":search",
+    ":sync_index",
+    ":sync_log",
+
+    #":sync_node_browser",
+    #":sync_search",
+    ":traffic_log",
+
+    #":types",
+    ":user_events",
+  ]
+}
+
+js_library("about") {
+  deps = [
+    "//third_party/jstemplate:jstemplate",
+    "//ui/webui/resources/js:util",
+  ]
+}
+
+js_library("chrome_sync") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:util",
+  ]
+}
+
+js_library("data") {
+}
+
+js_library("events") {
+  deps = [
+    "//third_party/jstemplate:jstemplate",
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
+js_library("search") {
+  deps = [
+    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js/cr/ui:splitter",
+  ]
+}
+
+js_library("sync_index") {
+  deps = [
+    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js/cr/ui:tabs",
+  ]
+}
+
+js_library("sync_log") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js/cr:event_target",
+  ]
+}
+
+js_library("sync_node_browser") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js/cr/ui:tree",
+  ]
+}
+
+js_library("sync_search") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js/cr/ui:array_data_model",
+    "//ui/webui/resources/js/cr/ui:list",
+  ]
+}
+
+js_library("traffic_log") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
+js_library("types") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
+js_library("user_events") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:util",
+  ]
+}
diff --git a/components/sync/driver/resources/chrome_sync.js b/components/sync/driver/resources/chrome_sync.js
index cbaca276..2951f67 100644
--- a/components/sync/driver/resources/chrome_sync.js
+++ b/components/sync/driver/resources/chrome_sync.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 // require cr.js
-// require cr/event_target.js
 // require cr/util.js
 
 cr.define('chrome.sync', function() {
@@ -97,7 +96,8 @@
    * Updates the logic sending events to the protocol logic if they should
    * include specifics or not when converting to a human readable format.
    *
-   * @param {bool} includeSpecifics Whether protocol events include specifics.
+   * @param {boolean} includeSpecifics Whether protocol events include
+   *     specifics.
    */
   var setIncludeSpecifics = function(includeSpecifics) {
     chrome.send('setIncludeSpecifics', [includeSpecifics]);
@@ -150,7 +150,7 @@
   /**
    * A map from counter values to asynchronous request callbacks.
    * Used in the implementation of GetAllNodes.
-   * @type {{number: !Function}}
+   * @type {!Object<!Function>}
    */
   var requestCallbacks = {};
 
@@ -174,7 +174,7 @@
    */
   var getAllNodesCallback = function(id, response) {
     requestCallbacks[id](response);
-    requestCallbacks[id] = undefined;
+    delete requestCallbacks[id];
   };
 
   return {
diff --git a/components/sync/driver/resources/data.js b/components/sync/driver/resources/data.js
index baca0554..6607193 100644
--- a/components/sync/driver/resources/data.js
+++ b/components/sync/driver/resources/data.js
@@ -52,14 +52,14 @@
 function versionToDateString(version) {
   // TODO(mmontgomery): ugly? Hacky? Is there a better way?
   var epochLength = Date.now().toString().length;
-  var epochTime = parseInt(version.slice(0, epochLength));
+  var epochTime = parseInt(version.slice(0, epochLength), 10);
   var date = new Date(epochTime);
   return date.toString();
 }
 
 /**
  * @param {!Object} node A JavaScript represenation of a sync entity.
- * @return {string} A string representation of the sync entity.
+ * @return {!Array<string>} A string representation of the sync entity.
  */
 function serializeNode(node) {
   return allFields.map(function(field) {
diff --git a/components/sync/driver/resources/index.html b/components/sync/driver/resources/index.html
index 58b52b4..eb06cf8 100644
--- a/components/sync/driver/resources/index.html
+++ b/components/sync/driver/resources/index.html
@@ -38,16 +38,16 @@
 <script src="chrome://resources/js/cr/ui/tabs.js"></script>
 <script src="chrome://resources/js/cr/ui/tree.js"></script>
 <script src="chrome://resources/js/util.js"></script>
-<script src="chrome://sync-internals/chrome_sync.js"></script>
-<script src="chrome://sync-internals/about.js"></script>
-<script src="chrome://sync-internals/events.js"></script>
-<script src="chrome://sync-internals/types.js"></script>
-<script src="chrome://sync-internals/sync_log.js"></script>
-<script src="chrome://sync-internals/sync_node_browser.js"></script>
-<script src="chrome://sync-internals/sync_search.js"></script>
-<script src="chrome://sync-internals/user_events.js"></script>
-<script src="chrome://sync-internals/traffic_log.js"></script>
-<script src="chrome://sync-internals/strings.js"></script>
+<script src="chrome_sync.js"></script>
+<script src="about.js"></script>
+<script src="events.js"></script>
+<script src="types.js"></script>
+<script src="sync_log.js"></script>
+<script src="sync_node_browser.js"></script>
+<script src="sync_search.js"></script>
+<script src="user_events.js"></script>
+<script src="traffic_log.js"></script>
+<script src="strings.js"></script>
 </head>
 <body>
 
@@ -103,6 +103,6 @@
 </tabbox>
 
 <script src="chrome://resources/js/jstemplate_compiled.js"></script>
-<script src="chrome://sync-internals/sync_index.js"></script>
+<script src="sync_index.js"></script>
 </body>
 </html>
diff --git a/components/sync/driver/resources/search.js b/components/sync/driver/resources/search.js
index 6f9816de..16f913e 100644
--- a/components/sync/driver/resources/search.js
+++ b/components/sync/driver/resources/search.js
@@ -7,16 +7,14 @@
 
 cr.ui.decorate('#sync-results-splitter', cr.ui.Splitter);
 
-var allLinks = document.getElementsByClassName('sync-search-quicklink');
-
 chrome.sync.decorateQuickQueryControls(
-  allLinks,
-  $('sync-search-submit'),
-  $('sync-search-query'));
+  document.getElementsByClassName('sync-search-quicklink'),
+  /** @type {!HTMLButtonElement} */ ($('sync-search-submit')),
+  /** @type {!HTMLInputElement} */ ($('sync-search-query')));
 
 chrome.sync.decorateSearchControls(
-  $('sync-search-query'),
-  $('sync-search-submit'),
-  $('sync-search-status'),
-  $('sync-results-list'),
-  $('sync-result-details'));
+  /** @type {!HTMLInputElement} */ ($('sync-search-query')),
+  /** @type {!HTMLButtonElement} */ ($('sync-search-submit')),
+  getRequiredElement('sync-search-status'),
+  getRequiredElement('sync-results-list'),
+  /** @type {!HTMLPreElement} */ ($('sync-result-details')));
diff --git a/components/sync/driver/resources/sync_log.js b/components/sync/driver/resources/sync_log.js
index 4ae094a..7435a18 100644
--- a/components/sync/driver/resources/sync_log.js
+++ b/components/sync/driver/resources/sync_log.js
@@ -54,7 +54,7 @@
 
       /**
        * The recorded log entries.
-       * @type {array}
+       * @type {!Array}
        */
       this.entries =  [];
 
@@ -83,7 +83,7 @@
      * field of a custom event.
      * @param {string} submodule The sync submodule for the event.
      * @param {string} event The name of the event.
-     * @param {dictionary} details A dictionary of event-specific details.
+     * @param {!Object} details A dictionary of event-specific details.
      */
     log_(submodule, event, details) {
       var entry = {
diff --git a/components/sync/driver/resources/sync_node_browser.js b/components/sync/driver/resources/sync_node_browser.js
index f236278..f33d3af4 100644
--- a/components/sync/driver/resources/sync_node_browser.js
+++ b/components/sync/driver/resources/sync_node_browser.js
@@ -63,7 +63,7 @@
 
   /**
    * Updates the 'Last refresh time' display.
-   * @param {string} The text to display.
+   * @param {string} str The text to display.
    */
   function setLastRefreshTime(str) {
     $('node-browser-refresh-time').textContent = str;
diff --git a/components/sync/driver/resources/sync_search.js b/components/sync/driver/resources/sync_search.js
index 48c0e08..7702c6c 100644
--- a/components/sync/driver/resources/sync_search.js
+++ b/components/sync/driver/resources/sync_search.js
@@ -21,18 +21,19 @@
   /**
    * Decorates the quick search controls
    *
-   * @param {Array of DOM elements} quickLinkArray The <a> object which
+   * @param {!NodeList<!Element>} quickLinkArray The <a> object which
    *     will be given a link to a quick filter option.
+   * @param {!HTMLButtonElement} submitControl
    * @param {!HTMLInputElement} queryControl The <input> object of
    *     type=search where user's query is typed.
    */
   var decorateQuickQueryControls = function(quickLinkArray, submitControl,
                                             queryControl) {
-    for (var index = 0; index < allLinks.length; ++index) {
-      var quickQuery = allLinks[index].getAttribute('data-query');
+    for (var index = 0; index < quickLinkArray.length; ++index) {
+      var quickQuery = quickLinkArray[index].getAttribute('data-query');
       var quickQueryFunction = createDoQueryFunction(queryControl,
           submitControl, quickQuery);
-      allLinks[index].addEventListener('click', quickQueryFunction);
+      quickLinkArray[index].addEventListener('click', quickQueryFunction);
     }
   };
 
@@ -40,7 +41,7 @@
    * Runs a search with the given query.
    *
    * @param {string} query The regex to do the search with.
-   * @param {function} callback The callback called with the search results;
+   * @param {!Function} callback The callback called with the search results.
    *     not called if doSearch() is called again while the search is running.
    */
   var doSearch = function(query, callback) {
@@ -76,7 +77,7 @@
    *     where the user can click to submit the query.
    * @param {!HTMLElement} statusControl The <span> object display the
    *     search status.
-   * @param {!HTMLElement} listControl The <list> object which holds
+   * @param {!HTMLElement} resultsControl The <list> object which holds
    *     the list of returned results.
    * @param {!HTMLPreElement} detailsControl The <pre> object which
    *     holds the details of the selected result.
diff --git a/components/sync/driver/resources/traffic_log.js b/components/sync/driver/resources/traffic_log.js
index 03530944..bfb297c3 100644
--- a/components/sync/driver/resources/traffic_log.js
+++ b/components/sync/driver/resources/traffic_log.js
@@ -6,6 +6,9 @@
   constructor() {
     this.protocolEvents = [];
     this.knownEventTimestamps = new Set();
+
+    /** @type {!HTMLElement} */
+    this.container;
   }
 
   /**
@@ -53,7 +56,7 @@
 
   /**
    * Toggles the given traffic event entry div's "expanded" state.
-   * @param {MouseEvent} e the click event that triggered the toggle.
+   * @param {!Event} e the click event that triggered the toggle.
    * @private
    */
   _expandListener(e) {
@@ -79,13 +82,13 @@
   }
 
   onLoad() {
-    this.container = $('traffic-event-fullscreen-container');
+    this.container = getRequiredElement('traffic-event-fullscreen-container');
 
     chrome.sync.events.addEventListener(
       'onProtocolEvent', this._onReceivedProtocolEvent.bind(this));
 
     // Make the prototype jscontent element disappear.
-    jstProcess({}, this.container);
+    jstProcess(new JsEvalContext({}), this.container);
   }
 });
 
diff --git a/components/sync/driver/resources/types.js b/components/sync/driver/resources/types.js
index d08cc0e..4195e28 100644
--- a/components/sync/driver/resources/types.js
+++ b/components/sync/driver/resources/types.js
@@ -59,7 +59,7 @@
     var counters = details.counters;
 
     if (typeCountersMap.hasOwnProperty(modelType))
-      for (k in counters) {
+      for (var k in counters) {
         typeCountersMap[modelType][k] = counters[k];
       }
     refreshTypeCountersDisplay();
diff --git a/components/sync_device_info/BUILD.gn b/components/sync_device_info/BUILD.gn
index d50134fb..9c8d02a2 100644
--- a/components/sync_device_info/BUILD.gn
+++ b/components/sync_device_info/BUILD.gn
@@ -12,6 +12,8 @@
     "device_count_metrics_provider.h",
     "device_info.cc",
     "device_info.h",
+    "device_info_prefs.cc",
+    "device_info_prefs.h",
     "device_info_sync_bridge.cc",
     "device_info_sync_bridge.h",
     "device_info_sync_service.cc",
@@ -43,6 +45,7 @@
   deps = [
     "//components/keyed_service/core",
     "//components/metrics",
+    "//components/prefs",
     "//components/version_info",
     "//ui/base",
   ]
@@ -91,6 +94,7 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
+    "//components/prefs:test_support",
     "//components/sync:test_support",
     "//components/version_info:version_string",
     "//testing/gmock",
diff --git a/components/sync_device_info/DEPS b/components/sync_device_info/DEPS
index 3f6ed12..0f2e78e 100644
--- a/components/sync_device_info/DEPS
+++ b/components/sync_device_info/DEPS
@@ -2,6 +2,7 @@
   "+chromeos",
   "+components/keyed_service",
   "+components/metrics",
+  "+components/prefs",
   "+components/sync/base",
   "+components/sync/driver",
   "+components/sync/model",
diff --git a/components/sync_device_info/device_info_prefs.cc b/components/sync_device_info/device_info_prefs.cc
new file mode 100644
index 0000000..787cc15
--- /dev/null
+++ b/components/sync_device_info/device_info_prefs.cc
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync_device_info/device_info_prefs.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+
+namespace syncer {
+namespace {
+
+// The GUID device info will use to to remember the most recently used past
+// cache GUIDs in order starting with the current cache GUID.
+const char kDeviceInfoRecentGUIDs[] = "sync.local_device_guids";
+
+// The max number of local device most recent cached GUIDSs that will be stored.
+const int kMaxLocalCacheGuidsStored = 30;
+
+}  // namespace
+
+// static
+void DeviceInfoPrefs::RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  registry->RegisterListPref(kDeviceInfoRecentGUIDs);
+}
+
+DeviceInfoPrefs::DeviceInfoPrefs(PrefService* pref_service)
+    : pref_service_(pref_service) {
+  DCHECK(pref_service);
+}
+
+DeviceInfoPrefs::~DeviceInfoPrefs() {}
+
+bool DeviceInfoPrefs::IsRecentLocalCacheGuid(
+    const std::string& cache_guid) const {
+  const base::Value::ListStorage& recent_local_cache_guids =
+      pref_service_->GetList(kDeviceInfoRecentGUIDs)->GetList();
+
+  return std::find(recent_local_cache_guids.begin(),
+                   recent_local_cache_guids.end(),
+                   base::Value(cache_guid)) != recent_local_cache_guids.end();
+}
+
+void DeviceInfoPrefs::AddLocalCacheGuid(const std::string& cache_guid) {
+  ListPrefUpdate update(pref_service_, kDeviceInfoRecentGUIDs);
+  base::ListValue* pref_data = update.Get();
+  base::Value::ListStorage* recent_local_cache_guids = &pref_data->GetList();
+
+  if (std::find(recent_local_cache_guids->begin(),
+                recent_local_cache_guids->end(),
+                base::Value(cache_guid)) != recent_local_cache_guids->end()) {
+    // Local cache GUID already known.
+    return;
+  }
+
+  recent_local_cache_guids->emplace(recent_local_cache_guids->begin(),
+                                    base::Value(cache_guid));
+  if (recent_local_cache_guids->size() > kMaxLocalCacheGuidsStored) {
+    recent_local_cache_guids->pop_back();
+  }
+}
+
+}  // namespace syncer
diff --git a/components/sync_device_info/device_info_prefs.h b/components/sync_device_info/device_info_prefs.h
new file mode 100644
index 0000000..23a9e61f
--- /dev/null
+++ b/components/sync_device_info/device_info_prefs.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_PREFS_H_
+#define COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_PREFS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+class PrefService;
+class PrefRegistrySimple;
+
+namespace syncer {
+
+// Use this for determining if a cache guid was recently used by this device.
+class DeviceInfoPrefs {
+ public:
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+  explicit DeviceInfoPrefs(PrefService* pref_service);
+  ~DeviceInfoPrefs();
+
+  bool IsRecentLocalCacheGuid(const std::string& cache_guid) const;
+  void AddLocalCacheGuid(const std::string& cache_guid);
+
+ private:
+  PrefService* const pref_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceInfoPrefs);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_PREFS_H_
diff --git a/components/sync_device_info/device_info_sync_bridge.cc b/components/sync_device_info/device_info_sync_bridge.cc
index c431797..97190831 100644
--- a/components/sync_device_info/device_info_sync_bridge.cc
+++ b/components/sync_device_info/device_info_sync_bridge.cc
@@ -22,6 +22,7 @@
 #include "components/sync/model/mutable_data_batch.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "components/sync/protocol/sync.pb.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_device_info/device_info_util.h"
 #include "components/sync_device_info/local_device_info_util.h"
 
@@ -127,10 +128,13 @@
 DeviceInfoSyncBridge::DeviceInfoSyncBridge(
     std::unique_ptr<MutableLocalDeviceInfoProvider> local_device_info_provider,
     OnceModelTypeStoreFactory store_factory,
-    std::unique_ptr<ModelTypeChangeProcessor> change_processor)
+    std::unique_ptr<ModelTypeChangeProcessor> change_processor,
+    std::unique_ptr<DeviceInfoPrefs> device_info_prefs)
     : ModelTypeSyncBridge(std::move(change_processor)),
-      local_device_info_provider_(std::move(local_device_info_provider)) {
+      local_device_info_provider_(std::move(local_device_info_provider)),
+      device_info_prefs_(std::move(device_info_prefs)) {
   DCHECK(local_device_info_provider_);
+  DCHECK(device_info_prefs_);
 
   // Provider must not be initialized, the bridge takes care.
   DCHECK(!local_device_info_provider_->GetLocalDeviceInfo());
@@ -150,6 +154,8 @@
     const DataTypeActivationRequest& request) {
   // Store the cache GUID, mainly in case MergeSyncData() is executed later.
   local_cache_guid_ = request.cache_guid;
+  // Add the cache guid to the local prefs.
+  device_info_prefs_->AddLocalCacheGuid(local_cache_guid_);
 }
 
 std::unique_ptr<MetadataChangeList>
@@ -174,8 +180,8 @@
     DCHECK_EQ(change->storage_key(), specifics.cache_guid());
 
     // Each device is the authoritative source for itself, ignore any remote
-    // changes that have our local cache guid.
-    if (change->storage_key() == local_cache_guid_) {
+    // changes that have a cache guid that is or was this local device.
+    if (device_info_prefs_->IsRecentLocalCacheGuid(change->storage_key())) {
       continue;
     }
 
@@ -197,8 +203,8 @@
   for (const std::unique_ptr<EntityChange>& change : entity_changes) {
     const std::string guid = change->storage_key();
     // Each device is the authoritative source for itself, ignore any remote
-    // changes that have our local cache guid.
-    if (guid == local_cache_guid_) {
+    // changes that have a cache guid that is or was this local device.
+    if (device_info_prefs_->IsRecentLocalCacheGuid(guid)) {
       continue;
     }
 
@@ -305,6 +311,11 @@
   return CountActiveDevices(Time::Now());
 }
 
+bool DeviceInfoSyncBridge::IsRecentLocalCacheGuid(
+    const std::string& cache_guid) const {
+  return device_info_prefs_->IsRecentLocalCacheGuid(cache_guid);
+}
+
 bool DeviceInfoSyncBridge::IsPulseTimerRunningForTest() const {
   return pulse_timer_.IsRunning();
 }
@@ -429,6 +440,10 @@
   local_cache_guid_ = local_cache_guid_in_metadata;
   local_device_info_provider_->Initialize(local_cache_guid_,
                                           local_session_name_);
+
+  // This probably isn't strictly needed, but in case the cache_guid has changed
+  // we save the new one to prefs.
+  device_info_prefs_->AddLocalCacheGuid(local_cache_guid_);
   ReconcileLocalAndStored();
 }
 
diff --git a/components/sync_device_info/device_info_sync_bridge.h b/components/sync_device_info/device_info_sync_bridge.h
index 0d220b9..8d0640e 100644
--- a/components/sync_device_info/device_info_sync_bridge.h
+++ b/components/sync_device_info/device_info_sync_bridge.h
@@ -28,6 +28,8 @@
 
 namespace syncer {
 
+class DeviceInfoPrefs;
+
 // Sync bridge implementation for DEVICE_INFO model type. Handles storage of
 // device info and associated sync metadata, applying/merging foreign changes,
 // and allows public read access.
@@ -38,7 +40,8 @@
       std::unique_ptr<MutableLocalDeviceInfoProvider>
           local_device_info_provider,
       OnceModelTypeStoreFactory store_factory,
-      std::unique_ptr<ModelTypeChangeProcessor> change_processor);
+      std::unique_ptr<ModelTypeChangeProcessor> change_processor,
+      std::unique_ptr<DeviceInfoPrefs> device_info_prefs);
   ~DeviceInfoSyncBridge() override;
 
   LocalDeviceInfoProvider* GetLocalDeviceInfoProvider();
@@ -67,6 +70,7 @@
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
   int CountActiveDevices() const override;
+  bool IsRecentLocalCacheGuid(const std::string& cache_guid) const override;
 
   // For testing only.
   bool IsPulseTimerRunningForTest() const;
@@ -139,6 +143,8 @@
   // Used to update our local device info once every pulse interval.
   base::OneShotTimer pulse_timer_;
 
+  const std::unique_ptr<DeviceInfoPrefs> device_info_prefs_;
+
   base::WeakPtrFactory<DeviceInfoSyncBridge> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(DeviceInfoSyncBridge);
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc
index 8a4357d..f8f8ad36 100644
--- a/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_task_environment.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/sync/base/time.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/data_type_activation_request.h"
@@ -22,6 +23,7 @@
 #include "components/sync/model/model_type_store_test_util.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "components/sync/test/test_matchers.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
@@ -220,6 +222,7 @@
  protected:
   DeviceInfoSyncBridgeTest()
       : store_(ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
+    DeviceInfoPrefs::RegisterProfilePrefs(pref_service_.registry());
     ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
   }
 
@@ -239,7 +242,8 @@
     bridge_ = std::make_unique<DeviceInfoSyncBridge>(
         std::make_unique<TestLocalDeviceInfoProvider>(),
         ModelTypeStoreTestUtil::FactoryForForwardingStore(store_.get()),
-        mock_processor_.CreateForwardingProcessor());
+        mock_processor_.CreateForwardingProcessor(),
+        std::make_unique<DeviceInfoPrefs>(&pref_service_));
     bridge_->AddObserver(this);
   }
 
@@ -402,6 +406,7 @@
   // Holds the store.
   const std::unique_ptr<ModelTypeStore> store_;
 
+  TestingPrefServiceSimple pref_service_;
   // Not initialized immediately (upon test's constructor). This allows each
   // test case to modify the dependencies the bridge will be constructed with.
   std::unique_ptr<DeviceInfoSyncBridge> bridge_;
diff --git a/components/sync_device_info/device_info_sync_service_impl.cc b/components/sync_device_info/device_info_sync_service_impl.cc
index 1fdade8..a2136a05 100644
--- a/components/sync_device_info/device_info_sync_service_impl.cc
+++ b/components/sync_device_info/device_info_sync_service_impl.cc
@@ -10,6 +10,7 @@
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "components/sync_device_info/device_info.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_device_info/device_info_sync_bridge.h"
 #include "components/sync_device_info/device_info_tracker.h"
 #include "components/sync_device_info/local_device_info_provider.h"
@@ -18,9 +19,10 @@
 
 DeviceInfoSyncServiceImpl::DeviceInfoSyncServiceImpl(
     OnceModelTypeStoreFactory model_type_store_factory,
-    std::unique_ptr<MutableLocalDeviceInfoProvider>
-        local_device_info_provider) {
+    std::unique_ptr<MutableLocalDeviceInfoProvider> local_device_info_provider,
+    std::unique_ptr<DeviceInfoPrefs> device_info_prefs) {
   DCHECK(local_device_info_provider);
+  DCHECK(device_info_prefs);
 
   // Make a copy of the channel to avoid relying on argument evaluation order.
   const version_info::Channel channel =
@@ -32,7 +34,8 @@
       std::make_unique<ClientTagBasedModelTypeProcessor>(
           DEVICE_INFO,
           /*dump_stack=*/base::BindRepeating(&ReportUnrecoverableError,
-                                             channel)));
+                                             channel)),
+      std::move(device_info_prefs));
 }
 
 DeviceInfoSyncServiceImpl::~DeviceInfoSyncServiceImpl() {}
diff --git a/components/sync_device_info/device_info_sync_service_impl.h b/components/sync_device_info/device_info_sync_service_impl.h
index be51046a..65cca99 100644
--- a/components/sync_device_info/device_info_sync_service_impl.h
+++ b/components/sync_device_info/device_info_sync_service_impl.h
@@ -13,15 +13,18 @@
 
 namespace syncer {
 
+class DeviceInfoPrefs;
 class DeviceInfoSyncBridge;
 class MutableLocalDeviceInfoProvider;
 
 class DeviceInfoSyncServiceImpl : public DeviceInfoSyncService {
  public:
   // |local_device_info_provider| must not be null.
+  // |device_info_prefs| must not be null.
   DeviceInfoSyncServiceImpl(OnceModelTypeStoreFactory model_type_store_factory,
                             std::unique_ptr<MutableLocalDeviceInfoProvider>
-                                local_device_info_provider);
+                                local_device_info_provider,
+                            std::unique_ptr<DeviceInfoPrefs> device_info_prefs);
   ~DeviceInfoSyncServiceImpl() override;
 
   // DeviceInfoSyncService implementation.
diff --git a/components/sync_device_info/device_info_tracker.h b/components/sync_device_info/device_info_tracker.h
index 50837d55..1d909fb35 100644
--- a/components/sync_device_info/device_info_tracker.h
+++ b/components/sync_device_info/device_info_tracker.h
@@ -44,6 +44,9 @@
   // A temporary function to to allow tests to ensure active devices.
   // TODO(crbug/948784) remove this function after architecture work.
   virtual void ForcePulseForTest() = 0;
+  // Returns if the provided |cache_guid| is the current device cache_guid for
+  // the current device or was of the recently used.
+  virtual bool IsRecentLocalCacheGuid(const std::string& cache_guid) const = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync_device_info/fake_device_info_tracker.cc b/components/sync_device_info/fake_device_info_tracker.cc
index 4ce4251..c13f12d 100644
--- a/components/sync_device_info/fake_device_info_tracker.cc
+++ b/components/sync_device_info/fake_device_info_tracker.cc
@@ -64,6 +64,16 @@
   NOTREACHED();
 }
 
+bool FakeDeviceInfoTracker::IsRecentLocalCacheGuid(
+    const std::string& cache_guid) const {
+  for (const DeviceInfo* device : devices_) {
+    if (device->guid() == cache_guid) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void FakeDeviceInfoTracker::Add(const DeviceInfo* device) {
   devices_.push_back(device);
 }
diff --git a/components/sync_device_info/fake_device_info_tracker.h b/components/sync_device_info/fake_device_info_tracker.h
index 933f875..d9fa2d27 100644
--- a/components/sync_device_info/fake_device_info_tracker.h
+++ b/components/sync_device_info/fake_device_info_tracker.h
@@ -32,6 +32,7 @@
   void RemoveObserver(Observer* observer) override;
   int CountActiveDevices() const override;
   void ForcePulseForTest() override;
+  bool IsRecentLocalCacheGuid(const std::string& cache_guid) const override;
 
   // Adds a new DeviceInfo entry to |devices_|.
   void Add(const DeviceInfo* device);
diff --git a/components/ui_devtools/page_agent.cc b/components/ui_devtools/page_agent.cc
index ec955f1..e371211a 100644
--- a/components/ui_devtools/page_agent.cc
+++ b/components/ui_devtools/page_agent.cc
@@ -4,34 +4,14 @@
 
 #include "components/ui_devtools/page_agent.h"
 
-#include "base/command_line.h"
-#include "components/ui_devtools/ui_element.h"
-
 namespace ui_devtools {
 
 PageAgent::PageAgent(DOMAgent* dom_agent) : dom_agent_(dom_agent) {}
 
 PageAgent::~PageAgent() {}
 
-void PaintRectVector(std::vector<UIElement*> child_elements) {
-  for (auto* element : child_elements) {
-    if (element->type() == UIElementType::VIEW) {
-      element->PaintRect();
-    }
-    PaintRectVector(element->children());
-  }
-}
-
-protocol::Response PageAgent::reload() {
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          "draw-view-bounds-rects")) {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        "draw-view-bounds-rects");
-  } else {
-    base::CommandLine::ForCurrentProcess()->InitFromArgv(
-        base::CommandLine::ForCurrentProcess()->argv());
-  }
-  PaintRectVector(dom_agent_->element_root()->children());
+protocol::Response PageAgent::reload(protocol::Maybe<bool> bypass_cache) {
+  NOTREACHED();
   return protocol::Response::OK();
 }
 
diff --git a/components/ui_devtools/page_agent.h b/components/ui_devtools/page_agent.h
index 9a985d5..5f3fa21 100644
--- a/components/ui_devtools/page_agent.h
+++ b/components/ui_devtools/page_agent.h
@@ -17,10 +17,12 @@
   ~PageAgent() override;
 
   // Called on Ctrl+R (windows, linux) or Meta+R (mac) from frontend, but used
-  // in UI Devtools to toggle the bounds debug rectangles for views.
-  protocol::Response reload() override;
+  // in UI Devtools to toggle the bounds debug rectangles for views. If called
+  // using Ctrl+Shift+R (windows, linux) or Meta+Shift+R (mac), |bypass_cache|
+  // will be true and will toggle bubble lock.
+  protocol::Response reload(protocol::Maybe<bool> bypass_cache) override;
 
- private:
+ protected:
   DOMAgent* const dom_agent_;
 
   DISALLOW_COPY_AND_ASSIGN(PageAgent);
diff --git a/components/ui_devtools/protocol.json b/components/ui_devtools/protocol.json
index b030b48..0f81199 100644
--- a/components/ui_devtools/protocol.json
+++ b/components/ui_devtools/protocol.json
@@ -707,7 +707,15 @@
                 },
                 {
                     "name": "reload",
-                    "description": "Using frontend reload command to toggle View debug rectangles"
+                    "parameters": [
+                        {
+                            "name":"ignoreCache",
+                            "type": "boolean",
+                             "optional": true,
+                            "description": "If true, browser cache is ignored (as if the user pressed Shift+refresh)."
+                        }
+                    ],
+                    "description": "Called on Ctrl+R with |ignoreCache|=false, which is used to toggle debug bounds rectangles and Ctrl+Shift+R with |ignoreCache|=true, which is used to toggle bubble locking."
                 }
             ],
             "description": "Actions and events related to the inspected page belong to the page domain.",
diff --git a/components/ui_devtools/views/BUILD.gn b/components/ui_devtools/views/BUILD.gn
index e364dad..d485d69 100644
--- a/components/ui_devtools/views/BUILD.gn
+++ b/components/ui_devtools/views/BUILD.gn
@@ -19,6 +19,8 @@
     "element_utility.h",
     "overlay_agent_views.cc",
     "overlay_agent_views.h",
+    "page_agent_views.cc",
+    "page_agent_views.h",
     "view_element.cc",
     "view_element.h",
     "widget_element.cc",
diff --git a/components/ui_devtools/views/devtools_server_util.cc b/components/ui_devtools/views/devtools_server_util.cc
index 2d0ad5e..18c0b08 100644
--- a/components/ui_devtools/views/devtools_server_util.cc
+++ b/components/ui_devtools/views/devtools_server_util.cc
@@ -12,6 +12,7 @@
 #include "components/ui_devtools/switches.h"
 #include "components/ui_devtools/views/dom_agent_views.h"
 #include "components/ui_devtools/views/overlay_agent_views.h"
+#include "components/ui_devtools/views/page_agent_views.h"
 
 namespace ui_devtools {
 
@@ -26,10 +27,10 @@
       std::make_unique<UiDevToolsClient>("UiDevToolsClient", server.get());
   auto dom_agent_views = DOMAgentViews::Create();
   auto* dom_agent_views_ptr = dom_agent_views.get();
+  client->AddAgent(std::make_unique<PageAgentViews>(dom_agent_views_ptr));
   client->AddAgent(std::move(dom_agent_views));
   client->AddAgent(std::make_unique<CSSAgent>(dom_agent_views_ptr));
   client->AddAgent(OverlayAgentViews::Create(dom_agent_views_ptr));
-  client->AddAgent(std::make_unique<PageAgent>(dom_agent_views_ptr));
   server->AttachClient(std::move(client));
   return server;
 }
diff --git a/components/ui_devtools/views/page_agent_views.cc b/components/ui_devtools/views/page_agent_views.cc
new file mode 100644
index 0000000..98a80219
--- /dev/null
+++ b/components/ui_devtools/views/page_agent_views.cc
@@ -0,0 +1,66 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/ui_devtools/views/page_agent_views.h"
+
+#include "base/command_line.h"
+#include "components/ui_devtools/ui_element.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/views_switches.h"
+
+namespace ui_devtools {
+
+PageAgentViews::PageAgentViews(DOMAgent* dom_agent) : PageAgent(dom_agent) {}
+
+PageAgentViews::~PageAgentViews() {}
+
+void PaintRectVector(std::vector<UIElement*> child_elements) {
+  for (auto* element : child_elements) {
+    if (element->type() == UIElementType::VIEW) {
+      element->PaintRect();
+    }
+    PaintRectVector(element->children());
+  }
+}
+
+protocol::Response PageAgentViews::disable() {
+  // Set bubble lock flag back to false.
+  views::BubbleDialogDelegateView::devtools_dismiss_override_ = false;
+
+  // Remove debug bounds rects if enabled.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          views::switches::kDrawViewBoundsRects)) {
+    base::CommandLine::ForCurrentProcess()->InitFromArgv(
+        base::CommandLine::ForCurrentProcess()->argv());
+    PaintRectVector(dom_agent_->element_root()->children());
+  }
+  return protocol::Response::OK();
+}
+
+protocol::Response PageAgentViews::reload(protocol::Maybe<bool> bypass_cache) {
+  if (!bypass_cache.isJust())
+    return protocol::Response::OK();
+
+  bool shift_pressed = bypass_cache.fromMaybe(false);
+
+  // Ctrl+Shift+R called to toggle bubble lock.
+  if (shift_pressed) {
+    views::BubbleDialogDelegateView::devtools_dismiss_override_ =
+        !views::BubbleDialogDelegateView::devtools_dismiss_override_;
+  } else {
+    // Ctrl+R called to toggle debug bounds rectangles.
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            views::switches::kDrawViewBoundsRects)) {
+      base::CommandLine::ForCurrentProcess()->InitFromArgv(
+          base::CommandLine::ForCurrentProcess()->argv());
+    } else {
+      base::CommandLine::ForCurrentProcess()->AppendSwitch(
+          views::switches::kDrawViewBoundsRects);
+    }
+    PaintRectVector(dom_agent_->element_root()->children());
+  }
+  return protocol::Response::OK();
+}
+
+}  // namespace ui_devtools
diff --git a/components/ui_devtools/views/page_agent_views.h b/components/ui_devtools/views/page_agent_views.h
new file mode 100644
index 0000000..573ce5a4
--- /dev/null
+++ b/components/ui_devtools/views/page_agent_views.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UI_DEVTOOLS_VIEWS_PAGE_AGENT_VIEWS_H_
+#define COMPONENTS_UI_DEVTOOLS_VIEWS_PAGE_AGENT_VIEWS_H_
+
+#include "components/ui_devtools/page_agent.h"
+
+namespace ui_devtools {
+
+class PageAgentViews : public PageAgent {
+ public:
+  explicit PageAgentViews(DOMAgent* dom_agent);
+  ~PageAgentViews() override;
+
+  // PageAgent:
+  protocol::Response disable() override;
+  protocol::Response reload(protocol::Maybe<bool> bypass_cache) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PageAgentViews);
+};
+
+}  // namespace ui_devtools
+
+#endif  // COMPONENTS_UI_DEVTOOLS_VIEWS_PAGE_AGENT_VIEWS_H_
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index f68520d..94e0d6d 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -918,6 +918,8 @@
       StartBrowserThreadPool();
     }
 
+    tracing::InitTracingPostThreadPoolStart();
+
     BrowserTaskExecutor::PostFeatureListSetup();
 
     delegate_->PostTaskSchedulerStart();
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 4f44dae..fa11692 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -184,7 +184,6 @@
     "//services/tracing/public/cpp",
     "//services/video_capture:lib",
     "//services/video_capture/public/cpp",
-    "//services/video_capture/public/cpp:manifest",
     "//services/video_capture/public/mojom:constants",
     "//services/video_capture/public/uma",
     "//services/viz/privileged/interfaces",
@@ -1891,6 +1890,7 @@
     "url_loader_factory_getter.h",
     "utility_process_host.cc",
     "utility_process_host.h",
+    "video_capture_service.cc",
     "wake_lock/wake_lock_context_host.cc",
     "wake_lock/wake_lock_context_host.h",
     "wake_lock/wake_lock_service_impl.cc",
diff --git a/content/browser/background_sync/background_sync_context_impl.cc b/content/browser/background_sync/background_sync_context_impl.cc
index bf445e03..5edaf76 100644
--- a/content/browser/background_sync/background_sync_context_impl.cc
+++ b/content/browser/background_sync/background_sync_context_impl.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
+#include "url/origin.h"
 
 namespace content {
 
@@ -154,6 +155,16 @@
                      std::move(callback)));
 }
 
+void BackgroundSyncContextImpl::RevivePeriodicBackgroundSyncRegistrations(
+    url::Origin origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  base::PostTaskWithTraits(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&BackgroundSyncContextImpl::
+                         RevivePeriodicBackgroundSyncRegistrationsOnIOThread,
+                     this, std::move(origin)));
+}
+
 base::TimeDelta BackgroundSyncContextImpl::GetSoonestWakeupDeltaOnIOThread(
     blink::mojom::BackgroundSyncType sync_type,
     base::Time last_browser_wakeup_for_periodic_sync) {
@@ -176,6 +187,16 @@
   std::move(callback).Run(soonest_wakeup_delta);
 }
 
+void BackgroundSyncContextImpl::
+    RevivePeriodicBackgroundSyncRegistrationsOnIOThread(url::Origin origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!background_sync_manager_)
+    return;
+
+  background_sync_manager_->RevivePeriodicSyncRegistrations(std::move(origin));
+}
+
 void BackgroundSyncContextImpl::FireBackgroundSyncEvents(
     blink::mojom::BackgroundSyncType sync_type,
     base::OnceClosure done_closure) {
diff --git a/content/browser/background_sync/background_sync_context_impl.h b/content/browser/background_sync/background_sync_context_impl.h
index ba54a87..f65b557 100644
--- a/content/browser/background_sync/background_sync_context_impl.h
+++ b/content/browser/background_sync/background_sync_context_impl.h
@@ -74,6 +74,7 @@
       blink::mojom::BackgroundSyncType sync_type,
       base::Time last_browser_wakeup_for_periodic_sync,
       base::OnceCallback<void(base::TimeDelta)> callback) override;
+  void RevivePeriodicBackgroundSyncRegistrations(url::Origin origin) override;
 
  protected:
   friend class base::RefCountedDeleteOnSequence<BackgroundSyncContextImpl>;
@@ -114,6 +115,8 @@
       base::OnceCallback<void(base::TimeDelta)> callback,
       base::TimeDelta soonest_wakeup_delta);
 
+  void RevivePeriodicBackgroundSyncRegistrationsOnIOThread(url::Origin origin);
+
   // The services are owned by this. They're either deleted
   // during ShutdownOnIO or when the channel is closed via
   // *ServiceHadConnectionError. Only accessed on the IO thread.
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 3850348e..b12cde95 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -1388,6 +1388,9 @@
   if (registration.sync_state() != blink::mojom::BackgroundSyncState::PENDING)
     return false;
 
+  if (registration.is_suspended())
+    return false;
+
   if (clock_->Now() < registration.delay_until())
     return false;
 
@@ -1508,6 +1511,135 @@
   return soonest_wakeup_time;
 }
 
+void BackgroundSyncManager::RevivePeriodicSyncRegistrations(
+    url::Origin origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (disabled_)
+    return;
+
+  op_scheduler_.ScheduleOperation(
+      CacheStorageSchedulerOp::kBackgroundSync,
+      base::BindOnce(&BackgroundSyncManager::ReviveOriginImpl,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(origin),
+                     MakeEmptyCompletion()));
+}
+
+void BackgroundSyncManager::ReviveOriginImpl(url::Origin origin,
+                                             base::OnceClosure callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (disabled_) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  std::move(callback));
+    return;
+  }
+
+  // Create a list of registrations to revive.
+  std::vector<const BackgroundSyncRegistration*> to_revive;
+  std::map<const BackgroundSyncRegistration*, int64_t>
+      service_worker_registration_ids;
+
+  for (const auto& active_registration : active_registrations_) {
+    int64_t service_worker_registration_id = active_registration.first;
+    if (active_registration.second.origin != origin)
+      continue;
+
+    for (const auto& key_and_registration :
+         active_registration.second.registration_map) {
+      const BackgroundSyncRegistration* registration =
+          &key_and_registration.second;
+      if (!registration->is_suspended())
+        continue;
+
+      to_revive.push_back(registration);
+      service_worker_registration_ids[registration] =
+          service_worker_registration_id;
+    }
+  }
+
+  if (to_revive.empty()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  std::move(callback));
+    return;
+  }
+
+  base::RepeatingClosure received_new_delays_closure = base::BarrierClosure(
+      to_revive.size(),
+      base::BindOnce(
+          &BackgroundSyncManager::DidReceiveDelaysForSuspendedRegistrations,
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+
+  for (const auto* registration : to_revive) {
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE, {BrowserThread::UI},
+        base::BindOnce(
+            &GetNextEventDelay, service_worker_context_, *registration,
+            std::make_unique<BackgroundSyncParameters>(*parameters_)),
+        base::BindOnce(&BackgroundSyncManager::ReviveDidGetNextEventDelay,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       service_worker_registration_ids[registration],
+                       *registration, received_new_delays_closure));
+  }
+}
+
+void BackgroundSyncManager::ReviveDidGetNextEventDelay(
+    int64_t service_worker_registration_id,
+    BackgroundSyncRegistration registration,
+    base::OnceClosure done_closure,
+    base::TimeDelta delay) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (delay.is_max()) {
+    std::move(done_closure).Run();
+    return;
+  }
+
+  BackgroundSyncRegistration* active_registration =
+      LookupActiveRegistration(blink::mojom::BackgroundSyncRegistrationInfo(
+          service_worker_registration_id, registration.options()->tag,
+          registration.sync_type()));
+  if (!active_registration) {
+    std::move(done_closure).Run();
+    return;
+  }
+
+  active_registration->set_delay_until(clock_->Now() + delay);
+
+  StoreRegistrations(
+      service_worker_registration_id,
+      base::BindOnce(&BackgroundSyncManager::ReviveDidStoreRegistration,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     service_worker_registration_id, std::move(done_closure)));
+}
+
+void BackgroundSyncManager::ReviveDidStoreRegistration(
+    int64_t service_worker_registration_id,
+    base::OnceClosure done_closure,
+    blink::ServiceWorkerStatusCode status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
+    // The service worker registration is gone.
+    active_registrations_.erase(service_worker_registration_id);
+    std::move(done_closure).Run();
+  }
+
+  if (status != blink::ServiceWorkerStatusCode::kOk) {
+    DisableAndClearManager(std::move(done_closure));
+    return;
+  }
+
+  std::move(done_closure).Run();
+}
+
+void BackgroundSyncManager::DidReceiveDelaysForSuspendedRegistrations(
+    base::OnceClosure callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  ScheduleDelayedProcessingOfRegistrations(BackgroundSyncType::PERIODIC);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
+}
+
 void BackgroundSyncManager::ScheduleDelayedProcessingOfRegistrations(
     blink::mojom::BackgroundSyncType sync_type) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -1546,8 +1678,6 @@
   }
 
   // Find the registrations that are ready to run.
-  // TODO(crbug.com/925297): Periodically re-evaluate suspended Periodic Sync
-  // registrations.
   std::vector<blink::mojom::BackgroundSyncRegistrationInfoPtr> to_fire;
 
   for (auto& sw_reg_id_and_registrations : active_registrations_) {
diff --git a/content/browser/background_sync/background_sync_manager.h b/content/browser/background_sync/background_sync_manager.h
index 398cf71..19f67f5 100644
--- a/content/browser/background_sync/background_sync_manager.h
+++ b/content/browser/background_sync/background_sync_manager.h
@@ -177,6 +177,9 @@
   base::Time GetSoonestPeriodicSyncEventTimeForOrigin(
       const url::Origin& origin) const;
 
+  // Revive any pending periodic Background Sync registrations for |origin|.
+  void RevivePeriodicSyncRegistrations(url::Origin origin);
+
  protected:
   BackgroundSyncManager(
       scoped_refptr<ServiceWorkerContextWrapper> context,
@@ -398,6 +401,16 @@
   // Whether an event should be logged for debuggability, for |sync_type|.
   bool ShouldLogToDevTools(blink::mojom::BackgroundSyncType sync_type);
 
+  void ReviveOriginImpl(url::Origin origin, base::OnceClosure callback);
+  void ReviveDidGetNextEventDelay(int64_t service_worker_registration_id,
+                                  BackgroundSyncRegistration registration,
+                                  base::OnceClosure done_closure,
+                                  base::TimeDelta delay);
+  void ReviveDidStoreRegistration(int64_t service_worker_registration_id,
+                                  base::OnceClosure done_closure,
+                                  blink::ServiceWorkerStatusCode status);
+  void DidReceiveDelaysForSuspendedRegistrations(base::OnceClosure callback);
+
   base::OnceClosure MakeEmptyCompletion();
 
   blink::ServiceWorkerStatusCode CanEmulateSyncEvent(
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index 5674277..26c1f05 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -257,6 +257,15 @@
         sync_type, last_browser_wakeup_for_periodic_sync);
   }
 
+  void SuspendPeriodicSyncRegistrations(std::set<url::Origin> origins) {
+    GetController()->NoteSuspendedPeriodicSyncOrigins(std::move(origins));
+  }
+
+  void RevivePeriodicSyncRegistrations(url::Origin origin) {
+    GetController()->ReviveSuspendedPeriodicSyncOrigin(origin);
+    test_background_sync_manager()->RevivePeriodicSyncRegistrations(origin);
+  }
+
  protected:
   MOCK_METHOD1(OnEventReceived,
                void(const devtools::proto::BackgroundServiceEvent&));
@@ -1228,6 +1237,43 @@
   EXPECT_TRUE(GetRegistration(sync_options_2_));
 }
 
+TEST_F(BackgroundSyncManagerTest, TestSupensionAndRevival) {
+  InitPeriodicSyncEventTest();
+  auto thirteen_hours = base::TimeDelta::FromHours(13);
+  sync_options_2_.min_interval = thirteen_hours.InMilliseconds();
+  sync_options_1_.min_interval = thirteen_hours.InMilliseconds();
+
+  auto origin = url::Origin::Create(GURL(kScope1).GetOrigin());
+
+  SuspendPeriodicSyncRegistrations({origin});
+  EXPECT_TRUE(Register(sync_options_1_));
+  EXPECT_TRUE(GetRegistration(sync_options_1_));
+  EXPECT_TRUE(Register(sync_options_2_));
+  EXPECT_TRUE(GetRegistration(sync_options_2_));
+  EXPECT_EQ(0, periodic_sync_events_called_);
+
+  // Advance clock.
+  test_clock_.Advance(thirteen_hours);
+  FireReadyEvents();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, periodic_sync_events_called_);
+  EXPECT_TRUE(GetRegistration(sync_options_1_));
+  EXPECT_TRUE(GetRegistration(sync_options_2_));
+
+  RevivePeriodicSyncRegistrations(std::move(origin));
+  base::RunLoop().RunUntilIdle();
+
+  // Advance clock.
+  test_clock_.Advance(thirteen_hours);
+  FireReadyEvents();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, periodic_sync_events_called_);
+  EXPECT_TRUE(GetRegistration(sync_options_1_));
+  EXPECT_TRUE(GetRegistration(sync_options_2_));
+  Unregister(sync_options_1_);
+  Unregister(sync_options_2_);
+}
+
 TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptFails) {
   InitDelayedSyncEventTest();
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
diff --git a/content/browser/builtin_service_manifests.cc b/content/browser/builtin_service_manifests.cc
index 07e2029..5fb43e9 100644
--- a/content/browser/builtin_service_manifests.cc
+++ b/content/browser/builtin_service_manifests.cc
@@ -30,7 +30,6 @@
 #include "services/service_manager/public/cpp/manifest_builder.h"
 #include "services/shape_detection/public/cpp/manifest.h"
 #include "services/tracing/manifest.h"
-#include "services/video_capture/public/cpp/manifest.h"
 
 #if defined(OS_LINUX)
 #include "components/services/font/public/cpp/manifest.h"  // nogncheck
@@ -91,12 +90,6 @@
           resource_coordinator::GetManifest(),
           shape_detection::GetManifest(),
           tracing::GetManifest(),
-          video_capture::GetManifest(
-              features::IsVideoCaptureServiceEnabledForOutOfProcess()
-                  ? service_manager::Manifest::ExecutionMode::
-                        kOutOfProcessBuiltin
-                  : service_manager::Manifest::ExecutionMode::
-                        kInProcessBuiltin),
 #if defined(OS_LINUX)
           font_service::GetManifest(),
 #endif
diff --git a/content/browser/devtools/protocol/system_info_handler.cc b/content/browser/devtools/protocol/system_info_handler.cc
index 11fdeabe..4f2663f5 100644
--- a/content/browser/devtools/protocol/system_info_handler.cc
+++ b/content/browser/devtools/protocol/system_info_handler.cc
@@ -171,10 +171,21 @@
     }
   }
 
+  SystemInfo::ImageType image_type;
+  switch (profile.image_type) {
+    case gpu::ImageDecodeAcceleratorType::kJpeg:
+      image_type = SystemInfo::ImageTypeEnum::Jpeg;
+      break;
+    case gpu::ImageDecodeAcceleratorType::kWebP:
+      image_type = SystemInfo::ImageTypeEnum::Webp;
+      break;
+    case gpu::ImageDecodeAcceleratorType::kUnknown:
+      image_type = SystemInfo::ImageTypeEnum::Unknown;
+      break;
+  }
+
   return SystemInfo::ImageDecodeAcceleratorCapability::Create()
-      .SetImageType(profile.image_type == gpu::ImageDecodeAcceleratorType::kJpeg
-                        ? "JPEG"
-                        : "Unknown")
+      .SetImageType(image_type)
       .SetMaxDimensions(GfxSizeToSystemInfoSize(profile.max_encoded_dimensions))
       .SetMinDimensions(GfxSizeToSystemInfoSize(profile.min_encoded_dimensions))
       .SetSubsamplings(std::move(subsamplings))
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index cfaf5da..8a61f6c 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -548,7 +548,6 @@
     if (base::FeatureList::IsEnabled(features::kMojoVideoCapture)) {
       video_capture_provider = std::make_unique<VideoCaptureProviderSwitcher>(
           std::make_unique<ServiceVideoCaptureProvider>(
-              GetSystemConnector(),
               base::BindRepeating(&SendVideoCaptureLogMessage)),
           InProcessVideoCaptureProvider::CreateInstanceForNonDeviceCapture(
               std::move(device_task_runner),
@@ -2055,13 +2054,13 @@
   if (!window_id)
     return;
 
-  if (video_type != MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE)
+  if (!blink::IsVideoDesktopCaptureMediaType(video_type))
     return;
 
   // Pass along for desktop screen and window capturing when
   // DesktopCaptureDevice is used.
   for (const MediaStreamDevice& device : devices) {
-    if (device.type != MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE)
+    if (!blink::IsVideoDesktopCaptureMediaType(device.type))
       continue;
 
     DesktopMediaID media_id = DesktopMediaID::Parse(device.id);
diff --git a/content/browser/renderer_host/media/ref_counted_video_source_provider.cc b/content/browser/renderer_host/media/ref_counted_video_source_provider.cc
index b02fcc94c..5d5e0dc 100644
--- a/content/browser/renderer_host/media/ref_counted_video_source_provider.cc
+++ b/content/browser/renderer_host/media/ref_counted_video_source_provider.cc
@@ -4,14 +4,14 @@
 
 #include "content/browser/renderer_host/media/ref_counted_video_source_provider.h"
 
+#include "content/public/browser/video_capture_service.h"
+
 namespace content {
 
 RefCountedVideoSourceProvider::RefCountedVideoSourceProvider(
     video_capture::mojom::VideoSourceProviderPtr source_provider,
-    video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider,
     base::OnceClosure destruction_cb)
     : source_provider_(std::move(source_provider)),
-      device_factory_provider_(std::move(device_factory_provider)),
       destruction_cb_(std::move(destruction_cb)) {}
 
 RefCountedVideoSourceProvider::~RefCountedVideoSourceProvider() {
@@ -23,12 +23,8 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-void RefCountedVideoSourceProvider::ShutdownServiceAsap() {
-  device_factory_provider_->ShutdownServiceAsap();
-}
-
 void RefCountedVideoSourceProvider::SetRetryCount(int32_t count) {
-  device_factory_provider_->SetRetryCount(count);
+  GetVideoCaptureService().SetRetryCount(count);
 }
 
 void RefCountedVideoSourceProvider::ReleaseProviderForTesting() {
diff --git a/content/browser/renderer_host/media/ref_counted_video_source_provider.h b/content/browser/renderer_host/media/ref_counted_video_source_provider.h
index 105843f3..d1b8eac 100644
--- a/content/browser/renderer_host/media/ref_counted_video_source_provider.h
+++ b/content/browser/renderer_host/media/ref_counted_video_source_provider.h
@@ -7,7 +7,6 @@
 
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
 #include "services/video_capture/public/mojom/video_source_provider.mojom.h"
 
 namespace content {
@@ -22,7 +21,6 @@
  public:
   RefCountedVideoSourceProvider(
       video_capture::mojom::VideoSourceProviderPtr source_provider,
-      video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider,
       base::OnceClosure destruction_cb);
 
   base::WeakPtr<RefCountedVideoSourceProvider> GetWeakPtr();
@@ -31,7 +29,6 @@
     return source_provider_;
   }
 
-  void ShutdownServiceAsap();
   void SetRetryCount(int32_t count);
   void ReleaseProviderForTesting();
 
@@ -40,7 +37,6 @@
   ~RefCountedVideoSourceProvider();
 
   video_capture::mojom::VideoSourceProviderPtr source_provider_;
-  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_;
   base::OnceClosure destruction_cb_;
   base::WeakPtrFactory<RefCountedVideoSourceProvider> weak_ptr_factory_{this};
 
diff --git a/content/browser/renderer_host/media/service_launched_video_capture_device.h b/content/browser/renderer_host/media/service_launched_video_capture_device.h
index b6a605e5..4f05f15 100644
--- a/content/browser/renderer_host/media/service_launched_video_capture_device.h
+++ b/content/browser/renderer_host/media/service_launched_video_capture_device.h
@@ -11,8 +11,8 @@
 
 namespace content {
 
-// Implementation of LaunchedVideoCaptureDevice that uses the "video_capture"
-// service.
+// Implementation of LaunchedVideoCaptureDevice that uses
+// video_capture::mojom::VideoCaptureService.
 class ServiceLaunchedVideoCaptureDevice : public LaunchedVideoCaptureDevice {
  public:
   ServiceLaunchedVideoCaptureDevice(
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher.h b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
index 7e87e646..c080c1a 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher.h
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher.h
@@ -13,8 +13,8 @@
 
 namespace content {
 
-// Implementation of VideoCaptureDeviceLauncher that uses the "video_capture"
-// service.
+// Implementation of VideoCaptureDeviceLauncher that uses uses
+// video_capture::mojom::VideoCaptureService.
 class CONTENT_EXPORT ServiceVideoCaptureDeviceLauncher
     : public VideoCaptureDeviceLauncher {
  public:
diff --git a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
index 6371542..a7e6ee8 100644
--- a/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc
@@ -62,7 +62,6 @@
         &mock_source_provider_, mojo::MakeRequest(&source_provider_));
     service_connection_ = base::MakeRefCounted<RefCountedVideoSourceProvider>(
         std::move(source_provider_),
-        video_capture::mojom::DeviceFactoryProviderPtr(),
         release_connection_cb_.Get());
 
     launcher_ = std::make_unique<ServiceVideoCaptureDeviceLauncher>(
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.cc b/content/browser/renderer_host/media/service_video_capture_provider.cc
index d32c8e6..c9da8b0 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider.cc
@@ -14,11 +14,11 @@
 #include "content/common/child_process_host_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/common/service_manager_connection.h"
+#include "content/public/browser/video_capture_service.h"
+#include "content/public/common/content_features.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 #include "services/video_capture/public/uma/video_capture_service_event.h"
 
 #if defined(OS_CHROMEOS)
@@ -51,38 +51,82 @@
 
 namespace content {
 
-ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
-    service_manager::Connector* connector,
-    base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
-    : connector_(connector ? connector->Clone() : nullptr),
-      emit_log_message_cb_(std::move(emit_log_message_cb)),
-      launcher_has_connected_to_source_provider_(false),
-      service_listener_binding_(this) {
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(
-          &ServiceVideoCaptureProvider::RegisterServiceListenerOnIOThread,
-          weak_ptr_factory_.GetWeakPtr()));
-}
+class ServiceVideoCaptureProvider::ServiceProcessObserver
+    : public ServiceProcessHost::Observer {
+ public:
+  ServiceProcessObserver(base::RepeatingClosure start_callback,
+                         base::RepeatingClosure stop_callback)
+      : io_task_runner_(
+            base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})),
+        start_callback_(std::move(start_callback)),
+        stop_callback_(std::move(stop_callback)) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    ServiceProcessHost::AddObserver(this);
+  }
+
+  ~ServiceProcessObserver() override {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+    ServiceProcessHost::RemoveObserver(this);
+  }
+
+ private:
+  // ServiceProcessHost::Observer implementation.
+  void OnServiceProcessLaunched(const ServiceProcessInfo& info) override {
+    if (info.IsService<video_capture::mojom::VideoCaptureService>())
+      io_task_runner_->PostTask(FROM_HERE, base::BindOnce(start_callback_));
+  }
+
+  void OnServiceProcessTerminatedNormally(
+      const ServiceProcessInfo& info) override {
+    if (info.IsService<video_capture::mojom::VideoCaptureService>())
+      io_task_runner_->PostTask(FROM_HERE, base::BindOnce(stop_callback_));
+  }
+
+  void OnServiceProcessCrashed(const ServiceProcessInfo& info) override {
+    if (info.IsService<video_capture::mojom::VideoCaptureService>())
+      io_task_runner_->PostTask(FROM_HERE, base::BindOnce(stop_callback_));
+  }
+
+  const scoped_refptr<base::TaskRunner> io_task_runner_;
+  const base::RepeatingClosure start_callback_;
+  const base::RepeatingClosure stop_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceProcessObserver);
+};
 
 #if defined(OS_CHROMEOS)
 ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
-    CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
-    service_manager::Connector* connector,
     base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
-    : connector_(connector ? connector->Clone() : nullptr),
-      create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)),
+    : ServiceVideoCaptureProvider(base::NullCallback(),
+                                  std::move(emit_log_message_cb)) {}
+
+ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
+    CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
+    base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
+    : create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)),
       emit_log_message_cb_(std::move(emit_log_message_cb)),
-      launcher_has_connected_to_source_provider_(false),
-      service_listener_binding_(this),
-      weak_ptr_factory_(this) {
-  base::PostTaskWithTraits(
-      FROM_HERE, {content::BrowserThread::IO},
-      base::BindOnce(
-          &ServiceVideoCaptureProvider::RegisterServiceListenerOnIOThread,
-          weak_ptr_factory_.GetWeakPtr()));
-}
+      launcher_has_connected_to_source_provider_(false) {
+#else   // defined(OS_CHROMEOS)
+ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
+    base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
+    : emit_log_message_cb_(std::move(emit_log_message_cb)),
+      launcher_has_connected_to_source_provider_(false) {
 #endif  // defined(OS_CHROMEOS)
+  if (features::IsVideoCaptureServiceEnabledForOutOfProcess()) {
+    service_process_observer_.emplace(
+        base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}),
+        base::BindRepeating(&ServiceVideoCaptureProvider::OnServiceStarted,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&ServiceVideoCaptureProvider::OnServiceStopped,
+                            weak_ptr_factory_.GetWeakPtr()));
+  } else if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) {
+    // Connect immediately and permanently when the service runs in-process.
+    base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})
+        ->PostTask(FROM_HERE,
+                   base::Bind(&ServiceVideoCaptureProvider::OnServiceStarted,
+                              weak_ptr_factory_.GetWeakPtr()));
+  }
+}
 
 ServiceVideoCaptureProvider::~ServiceVideoCaptureProvider() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@@ -105,13 +149,8 @@
           weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ServiceVideoCaptureProvider::OnServiceStarted(
-    const ::service_manager::Identity& identity,
-    uint32_t pid) {
+void ServiceVideoCaptureProvider::OnServiceStarted() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (identity.name() != video_capture::mojom::kServiceName)
-    return;
-
   // Whenever the video capture service starts, we register a
   // VirtualVideoCaptureDevicesChangedObserver in order to propagate device
   // change events when virtual devices are added to or removed from the
@@ -126,13 +165,9 @@
       true /*raise_event_if_virtual_devices_already_present*/);
 }
 
-void ServiceVideoCaptureProvider::OnServiceStopped(
-    const ::service_manager::Identity& identity) {
+void ServiceVideoCaptureProvider::OnServiceStopped() {
 #if defined(OS_MACOSX)
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  if (identity.name() != video_capture::mojom::kServiceName)
-    return;
-
   if (stashed_result_callback_for_retry_) {
     TRACE_EVENT_INSTANT0(
         TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
@@ -146,21 +181,6 @@
 #endif
 }
 
-void ServiceVideoCaptureProvider::RegisterServiceListenerOnIOThread() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-
-  if (!connector_)
-    return;
-
-  service_manager::mojom::ServiceManagerListenerPtr listener;
-  service_listener_binding_.Bind(mojo::MakeRequest(&listener));
-
-  service_manager::mojom::ServiceManagerPtr service_manager;
-  connector_->BindInterface(service_manager::mojom::kServiceName,
-                            &service_manager);
-  service_manager->AddListener(std::move(listener));
-}
-
 void ServiceVideoCaptureProvider::OnLauncherConnectingToSourceProvider(
     scoped_refptr<RefCountedVideoSourceProvider>* out_provider) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@@ -192,14 +212,8 @@
   launcher_has_connected_to_source_provider_ = false;
   time_of_last_connect_ = base::TimeTicks::Now();
 
-  DCHECK(connector_)
-      << "Attempted to connect to the video capture service from "
-         "a process that does not provide a "
-         "ServiceManagerConnection";
-  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider;
-  connector_->BindInterface(video_capture::mojom::kServiceName,
-                            &device_factory_provider);
-
+  auto ui_task_runner =
+      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI});
 #if defined(OS_CHROMEOS)
   video_capture::mojom::AcceleratorFactoryPtr accelerator_factory;
   if (!create_accelerator_factory_cb_)
@@ -207,18 +221,18 @@
         base::BindRepeating(&CreateAcceleratorFactory);
   mojo::MakeStrongBinding(create_accelerator_factory_cb_.Run(),
                           mojo::MakeRequest(&accelerator_factory));
-  device_factory_provider->InjectGpuDependencies(
+  GetVideoCaptureService().InjectGpuDependencies(
       std::move(accelerator_factory));
 #endif  // defined(OS_CHROMEOS)
 
   video_capture::mojom::VideoSourceProviderPtr source_provider;
-  device_factory_provider->ConnectToVideoSourceProvider(
+  GetVideoCaptureService().ConnectToVideoSourceProvider(
       mojo::MakeRequest(&source_provider));
   source_provider.set_connection_error_handler(base::BindOnce(
       &ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider,
       weak_ptr_factory_.GetWeakPtr()));
   auto result = base::MakeRefCounted<RefCountedVideoSourceProvider>(
-      std::move(source_provider), std::move(device_factory_provider),
+      std::move(source_provider),
       base::BindOnce(&ServiceVideoCaptureProvider::OnServiceConnectionClosed,
                      weak_ptr_factory_.GetWeakPtr(),
                      ReasonForDisconnect::kUnused));
@@ -274,13 +288,13 @@
       video_capture::uma::LogMacbookRetryGetDeviceInfosEvent(
           video_capture::uma::PROVIDER_RECEIVED_ZERO_INFOS_STOPPING_SERVICE);
       TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
-                           "Asking video capture service to shut down.",
+                           "Waiting for video capture service to shut down.",
                            TRACE_EVENT_SCOPE_PROCESS);
-      service_connection->ShutdownServiceAsap();
       stashed_result_callback_for_retry_ = std::move(result_callback);
       stashed_retry_count_ = retry_count;
-      // Continue when service manager reports that service has shut down via
-      // OnServiceStopped().
+
+      // We may try again once |OnServiceStopped()| is invoked via our
+      // ServiceProcessHost observer.
       return;
     }
   }
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.h b/content/browser/renderer_host/media/service_video_capture_provider.h
index 7318d84..71a3f6e 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.h
+++ b/content/browser/renderer_host/media/service_video_capture_provider.h
@@ -5,42 +5,38 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_SERVICE_VIDEO_CAPTURE_PROVIDER_H_
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_SERVICE_VIDEO_CAPTURE_PROVIDER_H_
 
+#include "base/threading/sequence_bound.h"
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/media/ref_counted_video_source_provider.h"
 #include "content/browser/renderer_host/media/video_capture_provider.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/mojom/service_manager.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "content/public/browser/service_process_host.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 
 namespace content {
 
-// Implementation of VideoCaptureProvider that uses the "video_capture" service.
+// Implementation of VideoCaptureProvider that uses
+// video_capture::mojom::VideoCaptureService.
+//
 // Connects to the service lazily on demand and disconnects from the service as
 // soon as all previously handed out VideoCaptureDeviceLauncher instances have
 // been released and no more answers to GetDeviceInfosAsync() calls are pending.
-// It is legal to create instances using |nullptr| as |connector| but for
-// instances produced this way, it is illegal to subsequently call any of the
-// public methods.
 class CONTENT_EXPORT ServiceVideoCaptureProvider
     : public VideoCaptureProvider,
-      public service_manager::mojom::ServiceManagerListener {
+      public ServiceProcessHost::Observer {
  public:
   // This constructor uses a default factory for instances of
   // viz::mojom::Gpu which produces instances of class content::GpuClient.
-  ServiceVideoCaptureProvider(
-      service_manager::Connector* connector,
+  explicit ServiceVideoCaptureProvider(
       base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);
 
 #if defined(OS_CHROMEOS)
   using CreateAcceleratorFactoryCallback = base::RepeatingCallback<
       std::unique_ptr<video_capture::mojom::AcceleratorFactory>()>;
-  // Lets clients provide a custom mojo::Connector and factory method for
-  // creating instances of viz::mojom::Gpu.
+  // Lets clients provide a custom factory method for creating instances of
+  // viz::mojom::Gpu.
   ServiceVideoCaptureProvider(
       CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
-      service_manager::Connector* connector,
       base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);
 #endif  // defined(OS_CHROMEOS)
 
@@ -50,25 +46,12 @@
   void GetDeviceInfosAsync(GetDeviceInfosCallback result_callback) override;
   std::unique_ptr<VideoCaptureDeviceLauncher> CreateDeviceLauncher() override;
 
-  // service_manager::mojom::ServiceManagerListener implementation.
-  void OnInit(std::vector<service_manager::mojom::RunningServiceInfoPtr>
-                  running_services) override {}
-  void OnServiceCreated(
-      service_manager::mojom::RunningServiceInfoPtr service) override {}
-  void OnServiceStarted(const ::service_manager::Identity& identity,
-                        uint32_t pid) override;
-  void OnServicePIDReceived(const ::service_manager::Identity& identity,
-                            uint32_t pid) override {}
-  void OnServiceFailedToStart(
-      const ::service_manager::Identity& identity) override {}
-  void OnServiceStopped(const ::service_manager::Identity& identity) override;
-
  private:
+  void OnServiceStarted();
+  void OnServiceStopped();
+
   enum class ReasonForDisconnect { kShutdown, kUnused, kConnectionLost };
 
-  void RegisterServiceListenerOnIOThread();
-  // Note, this needs to have void return value because of "weak_ptrs can only
-  // bind to methods without return values".
   void OnLauncherConnectingToSourceProvider(
       scoped_refptr<RefCountedVideoSourceProvider>* out_provider);
   // Discarding the returned RefCountedVideoSourceProvider indicates that the
@@ -91,7 +74,6 @@
   void OnLostConnectionToSourceProvider();
   void OnServiceConnectionClosed(ReasonForDisconnect reason);
 
-  std::unique_ptr<service_manager::Connector> connector_;
 #if defined(OS_CHROMEOS)
   CreateAcceleratorFactoryCallback create_accelerator_factory_cb_;
 #endif  // defined(OS_CHROMEOS)
@@ -103,14 +85,16 @@
   base::TimeTicks time_of_last_connect_;
   base::TimeTicks time_of_last_uninitialize_;
 
-  mojo::Binding<service_manager::mojom::ServiceManagerListener>
-      service_listener_binding_;
-
 #if defined(OS_MACOSX)
   GetDeviceInfosCallback stashed_result_callback_for_retry_;
   int stashed_retry_count_;
 #endif
 
+  // We own this but it must operate on the UI thread.
+  class ServiceProcessObserver;
+  base::Optional<base::SequenceBound<ServiceProcessObserver>>
+      service_process_observer_;
+
   base::WeakPtrFactory<ServiceVideoCaptureProvider> weak_ptr_factory_{this};
 };
 
diff --git a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
index ca82f24..41df3156 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
@@ -9,22 +9,20 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/mock_callback.h"
 #include "base/threading/thread.h"
 #include "content/public/browser/video_capture_device_launcher.h"
+#include "content/public/browser/video_capture_service.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/test/test_connector_factory.h"
-#include "services/video_capture/public/cpp/mock_device_factory_provider.h"
 #include "services/video_capture/public/cpp/mock_push_subscription.h"
+#include "services/video_capture/public/cpp/mock_video_capture_service.h"
 #include "services/video_capture/public/cpp/mock_video_source.h"
 #include "services/video_capture/public/cpp/mock_video_source_provider.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -40,49 +38,6 @@
 static const auto kIgnoreLogMessageCB =
     base::BindRepeating([](const std::string&) {});
 
-class FakeVideoCaptureService : public service_manager::Service {
- public:
-  explicit FakeVideoCaptureService(
-      service_manager::mojom::ServiceRequest request)
-      : binding_(this, std::move(request)) {}
-
-  void OnStart() {
-    registry_.AddInterface<video_capture::mojom::DeviceFactoryProvider>(
-        // Unretained |this| is safe because |registry_| is owned by |this|.
-        base::BindRepeating(
-            &FakeVideoCaptureService::OnDeviceFactoryProviderRequest,
-            base::Unretained(this)));
-  }
-
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override {
-    registry_.BindInterface(interface_name, std::move(interface_pipe));
-  }
-
-  void OnDeviceFactoryProviderRequest(
-      video_capture::mojom::DeviceFactoryProviderRequest request) {
-    BindFactoryProvider(&request);
-  }
-
-  MOCK_METHOD1(
-      BindFactoryProvider,
-      void(video_capture::mojom::DeviceFactoryProviderRequest* request));
-
- private:
-  service_manager::ServiceBinding binding_;
-  service_manager::BinderRegistry registry_;
-};
-
-class NullService : public service_manager::Service {
- public:
-  explicit NullService(service_manager::mojom::ServiceRequest request)
-      : binding_(this, std::move(request)) {}
-
- private:
-  service_manager::ServiceBinding binding_;
-};
-
 class MockVideoCaptureDeviceLauncherCallbacks
     : public VideoCaptureDeviceLauncher::Callbacks {
  public:
@@ -100,14 +55,13 @@
 class ServiceVideoCaptureProviderTest : public testing::Test {
  public:
   ServiceVideoCaptureProviderTest()
-      : fake_video_capture_service_(connector_factory_.RegisterInstance(
-            video_capture::mojom::kServiceName)),
-        fake_service_manager_(connector_factory_.RegisterInstance(
-            service_manager::mojom::kServiceName)),
-        factory_provider_binding_(&mock_device_factory_provider_),
-        factory_provider_is_bound_(false),
-        source_provider_binding_(&mock_source_provider_) {}
-  ~ServiceVideoCaptureProviderTest() override {}
+      : source_provider_binding_(&mock_source_provider_) {
+    OverrideVideoCaptureServiceForTesting(&mock_video_capture_service_);
+  }
+
+  ~ServiceVideoCaptureProviderTest() override {
+    OverrideVideoCaptureServiceForTesting(nullptr);
+  }
 
  protected:
   void SetUp() override {
@@ -116,28 +70,13 @@
         base::BindRepeating([]() {
           return std::unique_ptr<video_capture::mojom::AcceleratorFactory>();
         }),
-        connector_factory_.GetDefaultConnector(), kIgnoreLogMessageCB);
+        kIgnoreLogMessageCB);
 #else
-    provider_ = std::make_unique<ServiceVideoCaptureProvider>(
-        connector_factory_.GetDefaultConnector(), kIgnoreLogMessageCB);
+    provider_ =
+        std::make_unique<ServiceVideoCaptureProvider>(kIgnoreLogMessageCB);
 #endif  // defined(OS_CHROMEOS)
 
-    ON_CALL(fake_video_capture_service_, BindFactoryProvider(_))
-        .WillByDefault(Invoke(
-            [this](
-                video_capture::mojom::DeviceFactoryProviderRequest* request) {
-              if (factory_provider_binding_.is_bound())
-                factory_provider_binding_.Close();
-              factory_provider_binding_.Bind(std::move(*request));
-              factory_provider_is_bound_ = true;
-              factory_provider_binding_.set_connection_error_handler(
-                  base::BindOnce(
-                      [](bool* factory_provider_is_bound) {
-                        *factory_provider_is_bound = false;
-                      },
-                      &factory_provider_is_bound_));
-            }));
-    ON_CALL(mock_device_factory_provider_, DoConnectToVideoSourceProvider(_))
+    ON_CALL(mock_video_capture_service_, DoConnectToVideoSourceProvider(_))
         .WillByDefault(Invoke(
             [this](video_capture::mojom::VideoSourceProviderRequest& request) {
               if (source_provider_binding_.is_bound())
@@ -181,13 +120,7 @@
   void TearDown() override {}
 
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
-  service_manager::TestConnectorFactory connector_factory_;
-  FakeVideoCaptureService fake_video_capture_service_;
-  NullService fake_service_manager_;
-  video_capture::MockDeviceFactoryProvider mock_device_factory_provider_;
-  mojo::Binding<video_capture::mojom::DeviceFactoryProvider>
-      factory_provider_binding_;
-  bool factory_provider_is_bound_;
+  video_capture::MockVideoCaptureService mock_video_capture_service_;
   video_capture::MockVideoSourceProvider mock_source_provider_;
   mojo::Binding<video_capture::mojom::VideoSourceProvider>
       source_provider_binding_;
@@ -242,8 +175,6 @@
 
   // Simulate that the service goes down by cutting the connections.
   source_provider_binding_.Close();
-  factory_provider_binding_.Close();
-
   wait_for_callback_from_service.Run();
 }
 
@@ -273,13 +204,9 @@
   // Setup part 2: Now that the connection to the service is established, we can
   // listen for disconnects.
   base::RunLoop wait_for_connection_to_source_provider_to_close;
-  base::RunLoop wait_for_connection_to_device_factory_provider_to_close;
   source_provider_binding_.set_connection_error_handler(
       base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
                      &wait_for_connection_to_source_provider_to_close));
-  factory_provider_binding_.set_connection_error_handler(
-      base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
-                     &wait_for_connection_to_device_factory_provider_to_close));
 
   // Exercise part 2: The service responds
   std::vector<media::VideoCaptureDeviceInfo> arbitrarily_empty_results;
@@ -287,9 +214,6 @@
 
   // Verification: Expect |provider_| to close the connection to the service.
   wait_for_connection_to_source_provider_to_close.Run();
-  if (factory_provider_is_bound_) {
-    wait_for_connection_to_device_factory_provider_to_close.Run();
-  }
 }
 
 // Tests that |ServiceVideoCaptureProvider| does not close the connection to the
@@ -317,11 +241,6 @@
         *connection_has_been_closed = true;
       },
       &connection_has_been_closed));
-  factory_provider_binding_.set_connection_error_handler(base::BindOnce(
-      [](bool* connection_has_been_closed) {
-        *connection_has_been_closed = true;
-      },
-      &connection_has_been_closed));
 
   // Exercise part 2: Make a few GetDeviceInfosAsync requests
   base::RunLoop wait_for_get_device_infos_response_1;
@@ -413,11 +332,6 @@
         *connection_has_been_closed = true;
       },
       &connection_has_been_closed));
-  factory_provider_binding_.set_connection_error_handler(base::BindOnce(
-      [](bool* connection_has_been_closed) {
-        *connection_has_been_closed = true;
-      },
-      &connection_has_been_closed));
 
   // The service now responds to the first request.
   std::vector<media::VideoCaptureDeviceInfo> arbitrarily_empty_results;
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index 37d67c6..c5fa1a0 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -325,8 +325,7 @@
   DCHECK_EQ(controller, device_start_request_queue_.begin()->controller());
   DCHECK(controller);
 
-  if (controller->stream_type() ==
-      blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE) {
+  if (blink::IsVideoDesktopCaptureMediaType(controller->stream_type())) {
     const media::VideoCaptureSessionId session_id =
         device_start_request_queue_.front().session_id();
     DCHECK(session_id != kFakeSessionId);
@@ -619,8 +618,7 @@
     return;
   }
 
-  DCHECK_EQ(blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
-            existing_device->stream_type());
+  DCHECK(blink::IsVideoDesktopCaptureMediaType(existing_device->stream_type()));
   DesktopMediaID id = DesktopMediaID::Parse(existing_device->device_id());
   if (id.is_null())
     return;
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 8484cb5..d3d024f4 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -1828,7 +1828,7 @@
   if (target && target->ScreenRectIsUnstableFor(event))
     event.SetTargetFrameMovedRecently();
   if (blink::WebInputEvent::IsMouseEventType(event.GetType())) {
-    if (event.GetType() == blink::WebInputEvent::kMouseDown) {
+    if (target && event.GetType() == blink::WebInputEvent::kMouseDown) {
       mouse_down_post_transformed_coordinate_.SetPoint(target_location->x(),
                                                        target_location->y());
       last_mouse_down_target_ = target;
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index c747a95..2ac4547a 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -87,8 +87,6 @@
 #include "services/tracing/public/cpp/tracing_features.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/tracing_service.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
-#include "services/video_capture/service_impl.h"
 #include "ui/base/buildflags.h"
 #include "ui/base/ui_base_features.h"
 
@@ -319,13 +317,6 @@
       &LaunchInProcessService, std::move(task_runner), factory);
 }
 
-std::unique_ptr<service_manager::Service> CreateVideoCaptureService(
-    service_manager::mojom::ServiceRequest request) {
-  return std::make_unique<video_capture::ServiceImpl>(
-      std::move(request), base::CreateSingleThreadTaskRunnerWithTraits(
-                              {content::BrowserThread::UI}));
-}
-
 void CreateInProcessAudioService(
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     service_manager::mojom::ServiceRequest request) {
@@ -643,20 +634,6 @@
                              base::BindRepeating(&CreateMediaSessionService));
   }
 
-  if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) {
-    RegisterInProcessService(
-        video_capture::mojom::kServiceName,
-#if defined(OS_WIN)
-        base::CreateCOMSTATaskRunnerWithTraits(
-#else
-        base::CreateSingleThreadTaskRunnerWithTraits(
-#endif
-            base::TaskTraits({base::MayBlock(), base::WithBaseSyncPrimitives(),
-                              base::TaskPriority::BEST_EFFORT}),
-            base::SingleThreadTaskRunnerThreadMode::DEDICATED),
-        base::BindRepeating(&CreateVideoCaptureService));
-  }
-
 #if defined(OS_LINUX)
   RegisterInProcessService(
       font_service::mojom::kServiceName,
diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
index f41cf58..50034ead 100644
--- a/content/browser/service_process_host_impl.cc
+++ b/content/browser/service_process_host_impl.cc
@@ -173,6 +173,7 @@
                     : base::UTF8ToUTF16(*receiver.interface_name()));
   host->SetMetricsName(*receiver.interface_name());
   host->SetSandboxType(options.sandbox_type);
+  host->SetExtraCommandLineSwitches(std::move(options.extra_switches));
   if (options.child_flags)
     host->set_child_flags(*options.child_flags);
   host->Start();
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 7639658..0a50495 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -298,6 +298,11 @@
   service_identity_ = identity;
 }
 
+void UtilityProcessHost::SetExtraCommandLineSwitches(
+    std::vector<std::string> switches) {
+  extra_switches_ = std::move(switches);
+}
+
 mojom::ChildProcess* UtilityProcessHost::GetChildProcess() {
   return static_cast<ChildProcessHostImpl*>(process_->GetHost())
       ->child_process();
@@ -450,6 +455,9 @@
           *service_identity_, cmd_line.get());
     }
 
+    for (const auto& extra_switch : extra_switches_)
+      cmd_line->AppendSwitch(extra_switch);
+
     std::unique_ptr<UtilitySandboxedProcessLauncherDelegate> delegate =
         std::make_unique<UtilitySandboxedProcessLauncherDelegate>(
             sandbox_type_, env_, *cmd_line);
diff --git a/content/browser/utility_process_host.h b/content/browser/utility_process_host.h
index e3417c48..8233364 100644
--- a/content/browser/utility_process_host.h
+++ b/content/browser/utility_process_host.h
@@ -117,6 +117,9 @@
   // the identity of the service being launched.
   void SetServiceIdentity(const service_manager::Identity& identity);
 
+  // Provides extra switches to append to the process's command line.
+  void SetExtraCommandLineSwitches(std::vector<std::string> switches);
+
   // Returns a control interface for the running child process.
   mojom::ChildProcess* GetChildProcess();
 
@@ -161,6 +164,9 @@
   // service.
   base::Optional<service_manager::Identity> service_identity_;
 
+  // Extra command line switches to append.
+  std::vector<std::string> extra_switches_;
+
   // Indicates whether the process has been successfully launched yet, or if
   // launch failed.
   enum class LaunchState {
diff --git a/content/browser/video_capture_service.cc b/content/browser/video_capture_service.cc
new file mode 100644
index 0000000..4f9cdfb
--- /dev/null
+++ b/content/browser/video_capture_service.cc
@@ -0,0 +1,150 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/video_capture_service.h"
+
+#include "base/no_destructor.h"
+#include "base/task/post_task.h"
+#include "base/threading/sequence_local_storage_slot.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/service_process_host.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "services/video_capture/public/uma/video_capture_service_event.h"
+#include "services/video_capture/video_capture_service_impl.h"
+
+#if defined(OS_WIN)
+#define CREATE_IN_PROCESS_TASK_RUNNER base::CreateCOMSTATaskRunnerWithTraits
+#else
+#define CREATE_IN_PROCESS_TASK_RUNNER \
+  base::CreateSingleThreadTaskRunnerWithTraits
+#endif
+
+namespace content {
+
+namespace {
+
+video_capture::mojom::VideoCaptureService* g_service_override = nullptr;
+
+void BindInProcessInstance(
+    mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
+  static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service(
+      std::move(receiver),
+      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}));
+}
+
+mojo::Remote<video_capture::mojom::VideoCaptureService>& GetUIThreadRemote() {
+  static base::NoDestructor<
+      mojo::Remote<video_capture::mojom::VideoCaptureService>>
+      remote;
+  return *remote;
+}
+
+// This is a custom traits type we use in conjunction with mojo::ReceiverSetBase
+// so that all dispatched messages can be forwarded to the currently bound UI
+// thread Remote.
+struct ForwardingImplRefTraits {
+  using PointerType = void*;
+  static bool IsNull(PointerType) { return false; }
+  static video_capture::mojom::VideoCaptureService* GetRawPointer(PointerType) {
+    return &GetVideoCaptureService();
+  }
+};
+
+// If |GetVideoCaptureService()| is called from off the UI thread, return a
+// sequence-local Remote. Its corresponding receiver will be bound in this set,
+// forwarding to the current UI-thread Remote.
+void BindProxyRemoteOnUIThread(
+    mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
+  static base::NoDestructor<mojo::ReceiverSetBase<
+      mojo::Receiver<video_capture::mojom::VideoCaptureService,
+                     ForwardingImplRefTraits>,
+      void>>
+      receivers;
+  receivers->Add(nullptr, std::move(receiver));
+}
+
+}  // namespace
+
+video_capture::mojom::VideoCaptureService& GetVideoCaptureService() {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
+    static base::NoDestructor<base::SequenceLocalStorageSlot<
+        mojo::Remote<video_capture::mojom::VideoCaptureService>>>
+        storage;
+    auto& remote = storage->GetOrCreateValue();
+    if (!remote.is_bound()) {
+      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})
+          ->PostTask(FROM_HERE,
+                     base::BindOnce(&BindProxyRemoteOnUIThread,
+                                    remote.BindNewPipeAndPassReceiver()));
+    }
+    return *remote.get();
+  }
+
+  if (g_service_override)
+    return *g_service_override;
+
+  auto& remote = GetUIThreadRemote();
+  if (!remote.is_bound()) {
+    auto receiver = remote.BindNewPipeAndPassReceiver();
+    if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) {
+      auto dedicated_task_runner = CREATE_IN_PROCESS_TASK_RUNNER(
+          base::TaskTraits({base::MayBlock(), base::WithBaseSyncPrimitives(),
+                            base::TaskPriority::BEST_EFFORT}),
+          base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+      dedicated_task_runner->PostTask(
+          FROM_HERE,
+          base::BindOnce(&BindInProcessInstance, std::move(receiver)));
+    } else {
+      ServiceProcessHost::Launch(
+          std::move(receiver),
+          ServiceProcessHost::Options()
+              .WithDisplayName("Video Capture")
+              .WithSandboxType(service_manager::SANDBOX_TYPE_NO_SANDBOX)
+#if defined(OS_MACOSX)
+              // On Mac, the service requires a CFRunLoop which is provided by a
+              // UI message loop. See https://crbug.com/834581.
+              .WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi})
+#endif
+              .Pass());
+
+#if !defined(OS_ANDROID)
+      // On Android, we do not use automatic service shutdown, because when
+      // shutting down the service, we lose caching of the supported formats,
+      // and re-querying these can take several seconds on certain Android
+      // devices.
+      remote.set_idle_handler(
+          base::TimeDelta::FromSeconds(5),
+          base::BindRepeating(
+              [](mojo::Remote<video_capture::mojom::VideoCaptureService>*
+                     remote) {
+                video_capture::uma::LogVideoCaptureServiceEvent(
+                    video_capture::uma ::
+                        SERVICE_SHUTTING_DOWN_BECAUSE_NO_CLIENT);
+                remote->reset();
+              },
+              &remote));
+#endif  // !defined(OS_ANDROID)
+
+      // Make sure the Remote is also reset in case of e.g. service crash so we
+      // can restart it as needed.
+      remote.reset_on_disconnect();
+    }
+  }
+
+  return *remote.get();
+}
+
+void OverrideVideoCaptureServiceForTesting(
+    video_capture::mojom::VideoCaptureService* service) {
+  g_service_override = service;
+}
+
+}  // namespace content
diff --git a/content/browser/web_package/signed_exchange_loader_unittest.cc b/content/browser/web_package/signed_exchange_loader_unittest.cc
index 8280480..9f95ea7 100644
--- a/content/browser/web_package/signed_exchange_loader_unittest.cc
+++ b/content/browser/web_package/signed_exchange_loader_unittest.cc
@@ -149,13 +149,7 @@
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeLoaderTest);
 };
 
-// Test is flaky on Fuchsia. See https://crbug.com/986337.
-#if defined(OS_FUCHSIA)
-#define MAYBE_Simple DISABLED_Simple
-#else
-#define MAYBE_Simple Simple
-#endif
-TEST_P(SignedExchangeLoaderTest, MAYBE_Simple) {
+TEST_P(SignedExchangeLoaderTest, Simple) {
   network::mojom::URLLoaderPtr loader;
   network::mojom::URLLoaderClientPtr loader_client;
   MockURLLoader mock_loader(mojo::MakeRequest(&loader));
@@ -254,6 +248,7 @@
     ping_loader_client()->OnReceiveResponse(network::ResourceResponseHead());
     ping_loader_client()->OnComplete(
         network::URLLoaderCompletionStatus(net::OK));
+    run_loop.Run();
     base::RunLoop().RunUntilIdle();
   }
 }
diff --git a/content/browser/webrtc/webrtc_video_capture_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
index 03ad9fb..e2ad80c 100644
--- a/content/browser/webrtc/webrtc_video_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
@@ -7,7 +7,7 @@
 #include "build/build_config.h"
 #include "content/browser/webrtc/webrtc_webcam_browsertest.h"
 #include "content/public/browser/browser_child_process_host.h"
-#include "content/public/browser/system_connector.h"
+#include "content/public/browser/video_capture_service.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -16,8 +16,6 @@
 #include "content/shell/browser/shell.h"
 #include "media/base/media_switches.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/testing_controls.mojom.h"
 
 namespace content {
@@ -81,8 +79,8 @@
 
   // Simulate crash in video capture process
   video_capture::mojom::TestingControlsPtr service_controls;
-  GetSystemConnector()->BindInterface(video_capture::mojom::kServiceName,
-                                      mojo::MakeRequest(&service_controls));
+  GetVideoCaptureService().BindControlsForTesting(
+      mojo::MakeRequest(&service_controls));
   service_controls->Crash();
 
   // Wait for video element to turn black
diff --git a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
index 2dee5d94..9c76c55 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
@@ -10,7 +10,7 @@
 #include "cc/base/math_util.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/system_connector.h"
+#include "content/public/browser/video_capture_service.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
@@ -26,10 +26,8 @@
 #include "media/capture/video/shared_memory_handle_provider.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "services/video_capture/public/mojom/scoped_access_permission.mojom.h"
 #include "services/video_capture/public/mojom/virtual_device.mojom.h"
@@ -395,11 +393,11 @@
   base::WeakPtrFactory<SharedMemoryDeviceExerciser> weak_factory_{this};
 };
 
-// Integration test that obtains a connection to the video capture service via
-// the Browser process' service manager. It then registers a virtual device at
-// the service and feeds frames to it. It opens the virtual device in a <video>
-// element on a test page and verifies that the element plays in the expected
-// dimenstions and the pixel content on the element changes.
+// Integration test that obtains a connection to the video capture service. It
+// It then registers a virtual device at the service and feeds frames to it. It
+// opens the virtual device in a <video> element on a test page and verifies
+// that the element plays in the expected dimensions and the pixel content on
+// the element changes.
 class WebRtcVideoCaptureServiceBrowserTest : public ContentBrowserTest {
  public:
   WebRtcVideoCaptureServiceBrowserTest()
@@ -413,8 +411,14 @@
   void AddVirtualDeviceAndStartCapture(VirtualDeviceExerciser* device_exerciser,
                                        base::OnceClosure finish_test_cb) {
     DCHECK(virtual_device_thread_.task_runner()->RunsTasksInCurrentSequence());
-    connector_->BindInterface(video_capture::mojom::kServiceName, &provider_);
-    provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory_));
+
+    main_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(
+                       [](video_capture::mojom::DeviceFactoryRequest request) {
+                         GetVideoCaptureService().ConnectToDeviceFactory(
+                             std::move(request));
+                       },
+                       mojo::MakeRequest(&factory_)));
 
     media::VideoCaptureDeviceInfo info;
     info.descriptor.device_id = kVirtualDeviceId;
@@ -457,7 +461,6 @@
     LOG(INFO) << "Shutting down virtual device";
     device_exerciser->ShutDown();
     factory_ = nullptr;
-    provider_ = nullptr;
     weak_factory_.InvalidateWeakPtrs();
     std::move(continuation).Run();
   }
@@ -497,16 +500,10 @@
   void Initialize() {
     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
-
-    auto* connector = GetSystemConnector();
-    ASSERT_TRUE(connector);
-    // We need to clone it so that we can use the clone on a different thread.
-    connector_ = connector->Clone();
   }
 
   base::Thread virtual_device_thread_;
   scoped_refptr<base::TaskRunner> main_task_runner_;
-  std::unique_ptr<service_manager::Connector> connector_;
 
  private:
   base::TimeDelta CalculateTimeSinceFirstInvocation() {
@@ -516,7 +513,6 @@
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
-  video_capture::mojom::DeviceFactoryProviderPtr provider_;
   video_capture::mojom::DeviceFactoryPtr factory_;
   gfx::Size video_size_;
   base::TimeTicks first_frame_time_;
diff --git a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
index 7a991574..aa96d37 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
@@ -6,7 +6,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/system_connector.h"
+#include "content/public/browser/video_capture_service.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
@@ -16,11 +16,8 @@
 #include "media/base/media_switches.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "services/video_capture/public/cpp/mock_producer.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "services/video_capture/public/mojom/video_source_provider.mojom.h"
@@ -75,18 +72,18 @@
   ~WebRtcVideoCaptureServiceEnumerationBrowserTest() override {}
 
   void ConnectToService() {
-    connector_->BindInterface(video_capture::mojom::kServiceName, &provider_);
     video_capture::mojom::DevicesChangedObserverPtr observer;
     devices_changed_observer_binding_.Bind(mojo::MakeRequest(&observer));
     switch (GetParam().api_to_use) {
       case ServiceApi::kSingleClient:
-        provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory_));
+        GetVideoCaptureService().ConnectToDeviceFactory(
+            mojo::MakeRequest(&factory_));
         factory_->RegisterVirtualDevicesChangedObserver(
             std::move(observer),
             false /*raise_event_if_virtual_devices_already_present*/);
         break;
       case ServiceApi::kMultiClient:
-        provider_->ConnectToVideoSourceProvider(
+        GetVideoCaptureService().ConnectToVideoSourceProvider(
             mojo::MakeRequest(&video_source_provider_));
         video_source_provider_->RegisterVirtualDevicesChangedObserver(
             std::move(observer),
@@ -166,7 +163,6 @@
   void DisconnectFromService() {
     factory_ = nullptr;
     video_source_provider_ = nullptr;
-    provider_ = nullptr;
   }
 
   void EnumerateDevicesInRendererAndVerifyDeviceCount(
@@ -218,13 +214,8 @@
 
     NavigateToURL(shell(),
                   GURL(embedded_test_server()->GetURL(kVideoCaptureHtmlFile)));
-
-    auto* connector = GetSystemConnector();
-    ASSERT_TRUE(connector);
-    connector_ = connector->Clone();
   }
 
-  std::unique_ptr<service_manager::Connector> connector_;
   std::map<std::string, video_capture::mojom::TextureVirtualDevicePtr>
       texture_devices_by_id_;
   std::map<std::string,
@@ -236,7 +227,6 @@
   mojo::Binding<video_capture::mojom::DevicesChangedObserver>
       devices_changed_observer_binding_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  video_capture::mojom::DeviceFactoryProviderPtr provider_;
   video_capture::mojom::DeviceFactoryPtr factory_;
   video_capture::mojom::VideoSourceProviderPtr video_source_provider_;
   base::OnceClosure closure_to_be_called_on_devices_changed_;
diff --git a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
index af48c41..5b5ae1c4 100644
--- a/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_shared_device_browsertest.cc
@@ -6,19 +6,16 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
-#include "content/public/browser/system_connector.h"
+#include "content/public/browser/video_capture_service.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "media/base/media_switches.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "services/video_capture/public/cpp/mock_receiver.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
 #include "services/video_capture/public/mojom/video_source.mojom.h"
 #include "services/video_capture/public/mojom/video_source_provider.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -80,18 +77,16 @@
   ~WebRtcVideoCaptureSharedDeviceBrowserTest() override {}
 
   void OpenDeviceViaService() {
-    connector_->BindInterface(video_capture::mojom::kServiceName,
-                              &device_factory_provider_);
     switch (GetParam().api_to_use) {
       case ServiceApi::kSingleClient:
-        device_factory_provider_->ConnectToDeviceFactory(
+        GetVideoCaptureService().ConnectToDeviceFactory(
             mojo::MakeRequest(&device_factory_));
         device_factory_->GetDeviceInfos(base::BindOnce(
             &WebRtcVideoCaptureSharedDeviceBrowserTest::OnDeviceInfosReceived,
             weak_factory_.GetWeakPtr(), GetParam().buffer_type_to_request));
         break;
       case ServiceApi::kMultiClient:
-        device_factory_provider_->ConnectToVideoSourceProvider(
+        GetVideoCaptureService().ConnectToVideoSourceProvider(
             mojo::MakeRequest(&video_source_provider_));
         video_source_provider_->GetSourceInfos(base::BindOnce(
             &WebRtcVideoCaptureSharedDeviceBrowserTest::OnSourceInfosReceived,
@@ -130,18 +125,11 @@
   void Initialize() {
     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
-
-    auto* connector = GetSystemConnector();
-    ASSERT_TRUE(connector);
-    // We need to clone it so that we can use the clone on a different thread.
-    connector_ = connector->Clone();
-
     mock_receiver_ = std::make_unique<video_capture::MockReceiver>(
         mojo::MakeRequest(&receiver_proxy_));
   }
 
   scoped_refptr<base::TaskRunner> main_task_runner_;
-  std::unique_ptr<service_manager::Connector> connector_;
   std::unique_ptr<video_capture::MockReceiver> mock_receiver_;
 
  private:
@@ -204,7 +192,6 @@
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
-  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_;
 
   // For single-client API case only
   video_capture::mojom::DeviceFactoryPtr device_factory_;
diff --git a/content/public/android/java/src/org/chromium/content/app/ChromiumLinkerParams.java b/content/public/android/java/src/org/chromium/content/app/ChromiumLinkerParams.java
index 3ca2ef8a..2b2bf81 100644
--- a/content/public/android/java/src/org/chromium/content/app/ChromiumLinkerParams.java
+++ b/content/public/android/java/src/org/chromium/content/app/ChromiumLinkerParams.java
@@ -27,10 +27,6 @@
     // registered in the service process.
     public final String mTestRunnerClassNameForTesting;
 
-    // If mTestRunnerClassNameForTesting is not empty, the Linker implementation
-    // to force for testing.
-    public final int mLinkerImplementationForTesting;
-
     private static final String EXTRA_LINKER_PARAMS_BASE_LOAD_ADDRESS =
             "org.chromium.content.common.linker_params.base_load_address";
 
@@ -40,25 +36,20 @@
     private static final String EXTRA_LINKER_PARAMS_TEST_RUNNER_CLASS_NAME =
             "org.chromium.content.common.linker_params.test_runner_class_name";
 
-    private static final String EXTRA_LINKER_PARAMS_LINKER_IMPLEMENTATION =
-            "org.chromium.content.common.linker_params.linker_implementation";
-
     public ChromiumLinkerParams(long baseLoadAddress, boolean waitForSharedRelro) {
         mBaseLoadAddress = baseLoadAddress;
         mWaitForSharedRelro = waitForSharedRelro;
         mTestRunnerClassNameForTesting = null;
-        mLinkerImplementationForTesting = 0;
     }
 
     /**
      * Use this constructor to create a LinkerParams instance for testing.
      */
-    public ChromiumLinkerParams(long baseLoadAddress, boolean waitForSharedRelro,
-            String testRunnerClassName, int linkerImplementation) {
+    public ChromiumLinkerParams(
+            long baseLoadAddress, boolean waitForSharedRelro, String testRunnerClassName) {
         mBaseLoadAddress = baseLoadAddress;
         mWaitForSharedRelro = waitForSharedRelro;
         mTestRunnerClassNameForTesting = testRunnerClassName;
-        mLinkerImplementationForTesting = linkerImplementation;
     }
 
     /**
@@ -71,8 +62,7 @@
     public static ChromiumLinkerParams create(Bundle bundle) {
         if (!bundle.containsKey(EXTRA_LINKER_PARAMS_BASE_LOAD_ADDRESS)
                 || !bundle.containsKey(EXTRA_LINKER_PARAMS_WAIT_FOR_SHARED_RELRO)
-                || !bundle.containsKey(EXTRA_LINKER_PARAMS_TEST_RUNNER_CLASS_NAME)
-                || !bundle.containsKey(EXTRA_LINKER_PARAMS_LINKER_IMPLEMENTATION)) {
+                || !bundle.containsKey(EXTRA_LINKER_PARAMS_TEST_RUNNER_CLASS_NAME)) {
             return null;
         }
         return new ChromiumLinkerParams(bundle);
@@ -83,8 +73,6 @@
         mWaitForSharedRelro = bundle.getBoolean(EXTRA_LINKER_PARAMS_WAIT_FOR_SHARED_RELRO, false);
         mTestRunnerClassNameForTesting =
                 bundle.getString(EXTRA_LINKER_PARAMS_TEST_RUNNER_CLASS_NAME);
-        mLinkerImplementationForTesting =
-                bundle.getInt(EXTRA_LINKER_PARAMS_LINKER_IMPLEMENTATION, 0);
     }
 
     /**
@@ -97,7 +85,6 @@
         bundle.putBoolean(EXTRA_LINKER_PARAMS_WAIT_FOR_SHARED_RELRO, mWaitForSharedRelro);
         bundle.putString(
                 EXTRA_LINKER_PARAMS_TEST_RUNNER_CLASS_NAME, mTestRunnerClassNameForTesting);
-        bundle.putInt(EXTRA_LINKER_PARAMS_LINKER_IMPLEMENTATION, mLinkerImplementationForTesting);
     }
 
     // For debugging traces only.
@@ -105,8 +92,8 @@
     public String toString() {
         return String.format(Locale.US,
                 "LinkerParams(baseLoadAddress:0x%x, waitForSharedRelro:%s, "
-                        + "testRunnerClassName:%s, linkerImplementation:%d",
+                        + "testRunnerClassName:%s",
                 mBaseLoadAddress, Boolean.toString(mWaitForSharedRelro),
-                mTestRunnerClassNameForTesting, mLinkerImplementationForTesting);
+                mTestRunnerClassNameForTesting);
     }
 }
diff --git a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
index 4051516..b2013b5 100644
--- a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
+++ b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
@@ -81,7 +81,7 @@
         mCpuFeatures = connectionBundle.getLong(ContentChildProcessConstants.EXTRA_CPU_FEATURES);
         assert mCpuCount > 0;
 
-        if (LibraryLoader.useChromiumLinker() && !LibraryLoader.getInstance().isLoadedByZygote()) {
+        if (LibraryLoader.useCrazyLinker() && !LibraryLoader.getInstance().isLoadedByZygote()) {
             Bundle sharedRelros = connectionBundle.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
             if (sharedRelros != null) {
                 getLinker().useSharedRelros(sharedRelros);
@@ -107,7 +107,7 @@
 
         Linker linker = null;
         boolean requestedSharedRelro = false;
-        if (LibraryLoader.useChromiumLinker()) {
+        if (LibraryLoader.useCrazyLinker()) {
             assert mLinkerParams != null;
             linker = getLinker();
             if (mLinkerParams.mWaitForSharedRelro) {
@@ -187,8 +187,7 @@
             // For testing, set the Linker implementation and the test runner
             // class name to match those used by the parent.
             assert mLinkerParams != null;
-            Linker.setupForTesting(mLinkerParams.mLinkerImplementationForTesting,
-                    mLinkerParams.mTestRunnerClassNameForTesting);
+            Linker.setupForTesting(mLinkerParams.mTestRunnerClassNameForTesting);
         }
         return Linker.getInstance();
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
index 67d7bb7b..c5e7ff9 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelperImpl.java
@@ -132,7 +132,7 @@
                             ContentChildProcessConstants.EXTRA_CPU_COUNT, CpuFeatures.getCount());
                     connectionBundle.putLong(
                             ContentChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.getMask());
-                    if (LibraryLoader.useChromiumLinker()) {
+                    if (LibraryLoader.useCrazyLinker()) {
                         connectionBundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS,
                                 Linker.getInstance().getSharedRelros());
                     }
@@ -596,7 +596,7 @@
     private static void initLinker() {
         assert LauncherThread.runningOnLauncherThread();
         if (sLinkerInitialized) return;
-        if (LibraryLoader.useChromiumLinker()) {
+        if (LibraryLoader.useCrazyLinker()) {
             sLinkerLoadAddress = Linker.getInstance().getBaseLoadAddress();
             if (sLinkerLoadAddress == 0) {
                 Log.i(TAG, "Shared RELRO support disabled!");
@@ -617,8 +617,7 @@
         if (Linker.areTestsEnabled()) {
             Linker linker = Linker.getInstance();
             return new ChromiumLinkerParams(sLinkerLoadAddress, waitForSharedRelros,
-                    linker.getTestRunnerClassNameForTesting(),
-                    linker.getImplementationForTesting());
+                    linker.getTestRunnerClassNameForTesting());
         } else {
             return new ChromiumLinkerParams(sLinkerLoadAddress, waitForSharedRelros);
         }
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index a595c4a..c6bf6053 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -334,6 +334,7 @@
     "url_loader_request_interceptor.h",
     "video_capture_device_launcher.cc",
     "video_capture_device_launcher.h",
+    "video_capture_service.h",
     "visibility.h",
     "vpn_service_proxy.h",
     "web_contents.cc",
@@ -398,6 +399,7 @@
     "//services/resource_coordinator/public/cpp:resource_coordinator_cpp",
     "//services/service_manager/public/cpp",
     "//services/tracing/public/cpp",
+    "//services/video_capture/public/mojom",
     "//services/viz/public/interfaces",
     "//third_party/webrtc/modules/desktop_capture",
 
diff --git a/content/public/browser/background_sync_context.h b/content/public/browser/background_sync_context.h
index d2237b8..95d26d2 100644
--- a/content/public/browser/background_sync_context.h
+++ b/content/public/browser/background_sync_context.h
@@ -9,14 +9,14 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
+#include "url/origin.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
 #endif
 
-#include "third_party/blink/public/mojom/background_sync/background_sync.mojom.h"
-
 namespace content {
 
 class BrowserContext;
@@ -63,6 +63,10 @@
       base::Time last_browser_wakeup_for_periodic_sync,
       base::OnceCallback<void(base::TimeDelta)> callback) = 0;
 
+  // Revives any suspended periodic Background Sync registrations for |origin|.
+  virtual void RevivePeriodicBackgroundSyncRegistrations(
+      url::Origin origin) = 0;
+
  protected:
   virtual ~BackgroundSyncContext() = default;
 
diff --git a/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h b/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h
index 46f10dec..2f1f3a43 100644
--- a/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h
+++ b/content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h
@@ -6,7 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_CHROMEOS_DELEGATE_TO_BROWSER_GPU_SERVICE_ACCELERATOR_FACTORY_H_
 
 #include "content/common/content_export.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 
 namespace content {
 
diff --git a/content/public/browser/service_process_host.cc b/content/public/browser/service_process_host.cc
index c4ce6643..e03e0f4d 100644
--- a/content/public/browser/service_process_host.cc
+++ b/content/public/browser/service_process_host.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/public/browser/service_process_host.h"
+
+#include "base/strings/utf_string_conversions.h"
 #include "content/public/common/content_client.h"
 
 namespace content {
@@ -20,6 +22,12 @@
 }
 
 ServiceProcessHost::Options& ServiceProcessHost::Options::WithDisplayName(
+    const std::string& name) {
+  display_name = base::UTF8ToUTF16(name);
+  return *this;
+}
+
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithDisplayName(
     const base::string16& name) {
   display_name = name;
   return *this;
@@ -37,6 +45,13 @@
   return *this;
 }
 
+ServiceProcessHost::Options&
+ServiceProcessHost::Options::WithExtraCommandLineSwitches(
+    std::vector<std::string> switches) {
+  extra_switches = std::move(switches);
+  return *this;
+}
+
 ServiceProcessHost::Options ServiceProcessHost::Options::Pass() {
   return std::move(*this);
 }
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index 0f76b8be..25d3b3e3 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -6,6 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_SERVICE_PROCESS_HOST_H_
 
 #include <memory>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -58,6 +59,7 @@
     // Specifies the display name of the service process. This should generally
     // be a human readable and meaningful application or service name and will
     // appear in places like the system task viewer.
+    Options& WithDisplayName(const std::string& name);
     Options& WithDisplayName(const base::string16& name);
     Options& WithDisplayName(int resource_id);
 
@@ -65,6 +67,9 @@
     // ChildProcessHost for flag definitions.
     Options& WithChildFlags(int flags);
 
+    // Specifies extra command line switches to append before launch.
+    Options& WithExtraCommandLineSwitches(std::vector<std::string> switches);
+
     // Passes the contents of this Options object to a newly returned Options
     // value. This must be called when moving a built Options object into a call
     // to |Launch()|.
@@ -73,6 +78,7 @@
     SandboxType sandbox_type = service_manager::SANDBOX_TYPE_UTILITY;
     base::string16 display_name;
     base::Optional<int> child_flags;
+    std::vector<std::string> extra_switches;
   };
 
   // An interface which can be implemented and registered/unregistered with
diff --git a/content/public/browser/video_capture_service.h b/content/public/browser/video_capture_service.h
new file mode 100644
index 0000000..9753d1b
--- /dev/null
+++ b/content/public/browser/video_capture_service.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_PUBLIC_BROWSER_VIDEO_CAPTURE_SERVICE_H_
+#define CONTENT_PUBLIC_BROWSER_VIDEO_CAPTURE_SERVICE_H_
+
+#include "content/common/content_export.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
+
+namespace content {
+
+// Acquires a VideoCaptureService interface connected either to an in-process
+// instance or an out-of-process instance. If out-of-process, the service
+// process is launched lazily as needed and shut down when idle.
+//
+// This is callable from any thread, though when called from off of the UI
+// thread, messages sent on the interface will incur an extra thread hop before
+// going to the service.
+CONTENT_EXPORT video_capture::mojom::VideoCaptureService&
+GetVideoCaptureService();
+
+// Provides an override for the reference returned by
+// |GetVideoCaptureService()|. Call again with null to cancel the override
+// before |service| is destroyed.
+CONTENT_EXPORT void OverrideVideoCaptureServiceForTesting(
+    video_capture::mojom::VideoCaptureService* service);
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_VIDEO_CAPTURE_SERVICE_H_
diff --git a/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc b/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc
index a949d30e..8ae3a9e 100644
--- a/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc
+++ b/content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.cc
@@ -46,11 +46,6 @@
   //
   //   "/dev/ashmem/RELRO:<libname> (deleted)"
   //
-  // and for the ModernLinker, something like:
-  //
-  //   "/data/data/org.chromium.chromium_linker_test_apk/
-  //       app_chromium_linker_test/RELRO:<libname> (deleted)"
-  //
   // Where <libname> is the library name and '(deleted)' is actually
   // added by the kernel to indicate there is no corresponding file
   // on the filesystem.
@@ -59,7 +54,6 @@
   // section, but for the component build, there are several libraries,
   // each one with its own RELRO.
   static const char kLegacyRelroSectionPattern[] = "/dev/ashmem/RELRO:.*";
-  static const char kModernRelroSectionPattern[] = "/data/.*/RELRO:.*";
 
   // Parse /proc/self/maps and builds a list of region mappings in this
   // process.
@@ -78,7 +72,6 @@
   }
 
   const RE2 legacy_linker_re(kLegacyRelroSectionPattern);
-  const RE2 modern_linker_re(kModernRelroSectionPattern);
 
   int num_shared_relros = 0;
   int num_bad_shared_relros = 0;
@@ -88,15 +81,8 @@
 
     const std::string path = region.path;
     const bool is_legacy_relro = re2::RE2::FullMatch(path, legacy_linker_re);
-    const bool is_modern_relro = re2::RE2::FullMatch(path, modern_linker_re);
 
-    if (is_legacy_relro && is_modern_relro) {
-      LOG(ERROR) << prefix
-                 << "FAIL RELRO cannot be both Legacy and Modern (test error)";
-      return false;
-    }
-
-    if (!is_legacy_relro && !is_modern_relro) {
+    if (!is_legacy_relro) {
       // Ignore any mapping that isn't a shared RELRO.
       continue;
     }
@@ -127,16 +113,6 @@
       continue;
     }
 
-    // Shared RELROs implemented by ModernLinker are not in ashmem. ModernLinker
-    // (via android_dlopen_ext()) maps everything with MAP_PRIVATE rather than
-    // MAP_SHARED. Remapping such a RELRO section read-write will therefore
-    // succeed, but it is not a problem. The memory copy-on-writes, and updates
-    // are not visible to either the mapped file or other processes mapping the
-    // same file. So... we skip the remap test for ModernLinker.
-    if (is_modern_relro) {
-      continue;
-    }
-
     // Check that trying to remap it read-write fails with EACCES
     size_t region_size = region.end - region.start;
     int ret = ::mprotect(region_start, region_size, PROT_READ | PROT_WRITE);
diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java
index 046228a..8209c8cb 100644
--- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java
+++ b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java
@@ -36,8 +36,7 @@
         super.onCreate(savedInstanceState);
 
         // Setup the TestRunner class name.
-        Linker.setupForTesting(Linker.LINKER_IMPLEMENTATION_LEGACY,
-                "org.chromium.chromium_linker_test_apk.LinkerTests");
+        Linker.setupForTesting("org.chromium.chromium_linker_test_apk.LinkerTests");
 
         // Load the library in the browser process, this will also run the test
         // runner in this process.
diff --git a/content/test/mock_background_sync_controller.cc b/content/test/mock_background_sync_controller.cc
index d97a2677..0900d24 100644
--- a/content/test/mock_background_sync_controller.cc
+++ b/content/test/mock_background_sync_controller.cc
@@ -36,6 +36,9 @@
     BackgroundSyncParameters* parameters) {
   DCHECK(parameters);
 
+  if (suspended_periodic_sync_origins_.count(registration.origin()))
+    return base::TimeDelta::Max();
+
   int num_attempts = registration.num_attempts();
 
   if (!num_attempts) {
diff --git a/content/test/mock_background_sync_controller.h b/content/test/mock_background_sync_controller.h
index 36d2b1b3..9037b75 100644
--- a/content/test/mock_background_sync_controller.h
+++ b/content/test/mock_background_sync_controller.h
@@ -51,6 +51,9 @@
   BackgroundSyncParameters* background_sync_parameters() {
     return &background_sync_parameters_;
   }
+  void ReviveSuspendedPeriodicSyncOrigin(url::Origin origin_to_revive) {
+    suspended_periodic_sync_origins_.erase(origin_to_revive);
+  }
 
  private:
   int registration_count_ = 0;
diff --git a/content/utility/services.cc b/content/utility/services.cc
index 44e7b19c..39f3d532 100644
--- a/content/utility/services.cc
+++ b/content/utility/services.cc
@@ -6,7 +6,11 @@
 
 #include <utility>
 
+#include "base/no_destructor.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "content/public/utility/content_utility_client.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
+#include "services/video_capture/video_capture_service_impl.h"
 
 namespace content {
 
@@ -25,6 +29,13 @@
 }
 
 void HandleServiceRequestOnMainThread(mojo::GenericPendingReceiver receiver) {
+  if (auto video_capture_receiver =
+          receiver.As<video_capture::mojom::VideoCaptureService>()) {
+    static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service(
+        std::move(video_capture_receiver), base::ThreadTaskRunnerHandle::Get());
+    return;
+  }
+
   // If the request was handled already, we should not reach this point.
   DCHECK(receiver.is_valid());
   GetContentClient()->utility()->RunMainThreadService(std::move(receiver));
diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc
index e9c33602..e4c8405f 100644
--- a/content/utility/utility_service_factory.cc
+++ b/content/utility/utility_service_factory.cc
@@ -32,8 +32,6 @@
 #include "services/tracing/public/cpp/tracing_features.h"
 #include "services/tracing/public/mojom/constants.mojom.h"
 #include "services/tracing/tracing_service.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
-#include "services/video_capture/service_impl.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "media/cdm/cdm_adapter_factory.h"           // nogncheck
@@ -168,9 +166,6 @@
                        std::move(network_registry_),
                        base::SequencedTaskRunnerHandle::Get()));
     return;
-  } else if (service_name == video_capture::mojom::kServiceName) {
-    service = std::make_unique<video_capture::ServiceImpl>(
-        std::move(request), base::ThreadTaskRunnerHandle::Get());
   }
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   else if (service_name == media::mojom::kCdmServiceName) {
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_delegate.h b/extensions/browser/api/media_perception_private/media_perception_api_delegate.h
index 56e789f..0a82196 100644
--- a/extensions/browser/api/media_perception_private/media_perception_api_delegate.h
+++ b/extensions/browser/api/media_perception_private/media_perception_api_delegate.h
@@ -11,8 +11,8 @@
 #include "base/files/file_path.h"
 #include "chromeos/services/media_perception/public/mojom/media_perception_service.mojom.h"
 #include "extensions/common/api/media_perception_private.h"
-#include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
 
 namespace content {
 
@@ -40,12 +40,12 @@
       const api::media_perception_private::ComponentType& type,
       LoadCrOSComponentCallback load_callback) = 0;
 
-  // Provides an interface to the VideoCaptureService (started lazily by the
-  // Chrome service manager) to connect the MediaPerceptionService to it and
-  // establish a direct Mojo IPC-based connection.
-  // |provider| is owned by the caller.
-  virtual void BindDeviceFactoryProviderToVideoCaptureService(
-      video_capture::mojom::DeviceFactoryProviderPtr* provider) = 0;
+  // Provides an interface to the Video Capture service (started lazily by the
+  // browser) to connect the MediaPerceptionService to it and establish a direct
+  // Mojo IPC-based connection.
+  virtual void BindVideoSourceProvider(
+      mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider>
+          receiver) = 0;
 
   // Provides an interface to set a handler for an incoming
   // MediaPerceptionRequest.
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
index 2996a45..f73afc8 100644
--- a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
@@ -23,7 +23,6 @@
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
 
 namespace extensions {
 
@@ -97,9 +96,7 @@
   void ConnectToVideoCaptureService(
       video_capture::mojom::VideoSourceProviderRequest request) override {
     DCHECK(delegate_) << "Delegate not set.";
-    delegate_->BindDeviceFactoryProviderToVideoCaptureService(
-        &device_factory_provider_);
-    device_factory_provider_->ConnectToVideoSourceProvider(std::move(request));
+    delegate_->BindVideoSourceProvider(std::move(request));
   }
 
  private:
@@ -111,10 +108,6 @@
       chromeos::media_perception::mojom::MediaPerceptionControllerClient>
       binding_;
 
-  // Bound to the VideoCaptureService to establish the connection to the
-  // media analytics process.
-  video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_;
-
   DISALLOW_COPY_AND_ASSIGN(MediaPerceptionControllerClient);
 };
 
diff --git a/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc b/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
index 51c3644..76ecfa5 100644
--- a/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
+++ b/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
@@ -48,8 +48,9 @@
         base::BindOnce(std::move(load_callback), false, base::FilePath()));
   }
 
-  void BindDeviceFactoryProviderToVideoCaptureService(
-      video_capture::mojom::DeviceFactoryProviderPtr* provider) override {
+  void BindVideoSourceProvider(
+      mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver)
+      override {
     NOTIMPLEMENTED();
   }
 
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 445f5dd3..1a0a43a 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -1462,9 +1462,8 @@
 
   // TODO(http://crbug.com/980774): Investigate if this is necessary.
   if (!request->frame_data) {
-    content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
+    request->frame_data = ExtensionApiFrameIdMap::Get()->GetFrameData(
         request->render_process_id, request->frame_id);
-    request->frame_data = ExtensionApiFrameIdMap::Get()->GetFrameData(rfh);
   }
   event_details->SetFrameData(request->frame_data.value());
   DispatchEventToListeners(browser_context, std::move(listeners_to_dispatch),
diff --git a/extensions/browser/api/web_request/web_request_event_details.cc b/extensions/browser/api/web_request/web_request_event_details.cc
index b6415644..3668333 100644
--- a/extensions/browser/api/web_request/web_request_event_details.cc
+++ b/extensions/browser/api/web_request/web_request_event_details.cc
@@ -141,10 +141,9 @@
 
 void WebRequestEventDetails::DetermineFrameDataOnUI() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  content::RenderFrameHost* rfh =
-      content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
   ExtensionApiFrameIdMap::FrameData frame_data =
-      ExtensionApiFrameIdMap::Get()->GetFrameData(rfh);
+      ExtensionApiFrameIdMap::Get()->GetFrameData(render_process_id_,
+                                                  render_frame_id_);
   SetFrameData(frame_data);
 }
 
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index 8eb9e17..f2af091 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -213,8 +213,8 @@
     }
 
     // For subresource loads we attempt to resolve the FrameData immediately.
-    frame_data = ExtensionApiFrameIdMap::Get()->GetFrameData(
-        content::RenderFrameHost::FromID(render_process_id, frame_id));
+    frame_data = ExtensionApiFrameIdMap::Get()->GetFrameData(render_process_id,
+                                                             frame_id);
   }
 }
 
diff --git a/extensions/browser/extension_api_frame_id_map.cc b/extensions/browser/extension_api_frame_id_map.cc
index 071abda..5fa0ca5 100644
--- a/extensions/browser/extension_api_frame_id_map.cc
+++ b/extensions/browser/extension_api_frame_id_map.cc
@@ -188,36 +188,35 @@
 }
 
 ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::LookupFrameDataOnUI(
-    const RenderFrameIdKey& key) {
+    const RenderFrameIdKey& key,
+    bool check_deleted_frames) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  bool lookup_successful = false;
-  FrameData data;
   FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(key);
-  if (frame_id_iter != frame_data_map_.end()) {
-    lookup_successful = true;
-    data = frame_id_iter->second;
-  } else {
-    data = KeyToValue(key);
-    // Don't save invalid values in the map.
-    if (data.frame_id != kInvalidFrameId) {
-      lookup_successful = true;
-      auto kvpair = FrameDataMap::value_type(key, data);
-      frame_data_map_.insert(kvpair);
-    }
+
+  if (frame_id_iter != frame_data_map_.end())
+    return frame_id_iter->second;
+
+  if (check_deleted_frames) {
+    frame_id_iter = deleted_frame_data_map_.find(key);
+    if (frame_id_iter != deleted_frame_data_map_.end())
+      return frame_id_iter->second;
   }
+
+  FrameData data = KeyToValue(key);
+  // Don't save invalid values in the map.
+  if (data.frame_id != kInvalidFrameId)
+    frame_data_map_.insert({key, data});
+
   return data;
 }
 
 ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::GetFrameData(
-    content::RenderFrameHost* rfh) {
+    int render_process_id,
+    int render_frame_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  if (!rfh)
-    return FrameData();
-
-  const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
-  return LookupFrameDataOnUI(key);
+  const RenderFrameIdKey key(render_process_id, render_frame_id);
+  return LookupFrameDataOnUI(key, true /* check_deleted_frames */);
 }
 
 void ExtensionApiFrameIdMap::InitializeRenderFrameData(
@@ -227,21 +226,33 @@
   DCHECK(rfh->IsRenderFrameLive());
 
   const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
-  CacheFrameData(key);
+  LookupFrameDataOnUI(key, false /* check_deleted_frames */);
   DCHECK(frame_data_map_.find(key) != frame_data_map_.end());
 }
 
-void ExtensionApiFrameIdMap::CacheFrameData(const RenderFrameIdKey& key) {
-  LookupFrameDataOnUI(key);
-}
-
 void ExtensionApiFrameIdMap::OnRenderFrameDeleted(
     content::RenderFrameHost* rfh) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(rfh);
 
   const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
-  RemoveFrameData(key);
+  // TODO(http://crbug.com/522129): This is necessary right now because beacon
+  // requests made in window.onunload may start after this has been called.
+  // Delay the RemoveFrameData() call, so we will still have the frame data
+  // cached when the beacon request comes in.
+  auto iter = frame_data_map_.find(key);
+  if (iter == frame_data_map_.end())
+    return;
+
+  deleted_frame_data_map_.insert({key, iter->second});
+  frame_data_map_.erase(key);
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](ExtensionApiFrameIdMap* self, const RenderFrameIdKey& key) {
+            self->deleted_frame_data_map_.erase(key);
+          },
+          base::Unretained(this), key));
 }
 
 void ExtensionApiFrameIdMap::UpdateTabAndWindowId(
@@ -350,10 +361,4 @@
   return frame_data_map_.size();
 }
 
-void ExtensionApiFrameIdMap::RemoveFrameData(const RenderFrameIdKey& key) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  frame_data_map_.erase(key);
-}
-
 }  // namespace extensions
diff --git a/extensions/browser/extension_api_frame_id_map.h b/extensions/browser/extension_api_frame_id_map.h
index c259da1..c8566e43 100644
--- a/extensions/browser/extension_api_frame_id_map.h
+++ b/extensions/browser/extension_api_frame_id_map.h
@@ -123,10 +123,12 @@
       content::WebContents* web_contents,
       int frame_id);
 
-  // Retrieves the FrameData for a given |rfh|. The map may be updated with the
-  // result if the map did not contain the FrameData before the lookup.
-  // If |rfh| is nullptr, then the map is not modified.
-  FrameData GetFrameData(content::RenderFrameHost* rfh) WARN_UNUSED_RESULT;
+  // Retrieves the FrameData for a given |render_process_id| and
+  // |render_frame_id|. The map may be updated with the result if the map did
+  // not contain the FrameData before the lookup. If a RenderFrameHost is not
+  // found, then the map is not modified.
+  FrameData GetFrameData(int render_process_id,
+                         int render_frame_id) WARN_UNUSED_RESULT;
 
   // Initializes the FrameData for the given |rfh|.
   void InitializeRenderFrameData(content::RenderFrameHost* rfh);
@@ -199,13 +201,10 @@
   virtual FrameData KeyToValue(const RenderFrameIdKey& key) const;
 
   // Looks up the data for the given |key| and adds it to the |frame_data_map_|.
-  FrameData LookupFrameDataOnUI(const RenderFrameIdKey& key);
-
-  // Implementation of CacheFrameData(RenderFrameHost), separated for testing.
-  void CacheFrameData(const RenderFrameIdKey& key);
-
-  // Implementation of RemoveFrameData(RenderFrameHost), separated for testing.
-  void RemoveFrameData(const RenderFrameIdKey& key);
+  // If |check_deleted_frames| is true, |deleted_frame_data_map_| will also be
+  // used.
+  FrameData LookupFrameDataOnUI(const RenderFrameIdKey& key,
+                                bool check_deleted_frames);
 
   std::unique_ptr<ExtensionApiFrameIdMapHelper> helper_;
 
@@ -213,6 +212,11 @@
   // TODO(http://crbug.com/980774): Investigate if this is still needed.
   FrameDataMap frame_data_map_;
 
+  // Holds mappings of render frame key to FrameData from frames that have been
+  // recently deleted. These are kept for a short time so beacon requests that
+  // continue after a frame is unloaded can access the FrameData.
+  FrameDataMap deleted_frame_data_map_;
+
   // The set of pending main frame navigations for which ReadyToCommitNavigation
   // has been fired. Only used on the UI thread. This is needed to clear state
   // set up in OnMainFrameReadyToCommitNavigation for navigations which
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 0ad3e3c..8eb14c0 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -276,7 +276,8 @@
   },
   "management": [{
     "dependencies": ["permission:management"],
-    "contexts": ["blessed_extension", "extension_service_worker"],
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false,
     "default_parent": true
   }, {
     "channel": "stable",
@@ -446,9 +447,9 @@
     ],
     "contexts": [
       "blessed_extension",
-      "lock_screen_extension",
-      "extension_service_worker"
-    ]
+      "lock_screen_extension"
+    ],
+    "disallow_for_service_workers": false
   },
   "runtime.getManifest": {
     "contexts": [
@@ -466,32 +467,33 @@
       "blessed_extension",
       "lock_screen_extension",
       "unblessed_extension",
-      "web_page",
-      "extension_service_worker"
+      "web_page"
     ],
+    "disallow_for_service_workers": false,
     "matches": ["<all_urls>"]
   },
   "runtime.connectNative": {
     "dependencies": ["permission:nativeMessaging"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   },
   "runtime.getURL": {
     "contexts": [
       "blessed_extension",
       "lock_screen_extension",
       "unblessed_extension",
-      "content_script",
-      "extension_service_worker"
-    ]
+      "content_script"
+    ],
+    "disallow_for_service_workers": false
   },
   "runtime.id": {
     "contexts": [
       "blessed_extension",
       "lock_screen_extension",
       "unblessed_extension",
-      "content_script",
-      "extension_service_worker"
-    ]
+      "content_script"
+    ],
+    "disallow_for_service_workers": false
   },
   "runtime.lastError": {
     "contexts": "all",
@@ -503,9 +505,9 @@
       "blessed_extension",
       "lock_screen_extension",
       "unblessed_extension",
-      "content_script",
-      "extension_service_worker"
-    ]
+      "content_script"
+    ],
+    "disallow_for_service_workers": false
   },
   "runtime.onConnectNative": {
     "dependencies": ["permission:nativeMessaging"],
@@ -516,9 +518,9 @@
       "blessed_extension",
       "lock_screen_extension",
       "unblessed_extension",
-      "content_script",
-      "extension_service_worker"
-    ]
+      "content_script"
+    ],
+    "disallow_for_service_workers": false
   },
   "runtime.sendMessage": {
     // Everything except WebUI.
@@ -528,14 +530,15 @@
       "blessed_extension",
       "lock_screen_extension",
       "unblessed_extension",
-      "web_page",
-      "extension_service_worker"
+      "web_page"
     ],
+    "disallow_for_service_workers": false,
     "matches": ["<all_urls>"]
   },
   "runtime.sendNativeMessage": {
     "dependencies": ["permission:nativeMessaging"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   },
   "serial": {
     "dependencies": ["permission:serial"],
@@ -559,8 +562,8 @@
   },
   "storage": {
     "dependencies": ["permission:storage"],
-    "contexts": ["blessed_extension", "unblessed_extension", "content_script",
-                 "extension_service_worker"]
+    "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
+    "disallow_for_service_workers": false
   },
   "system.cpu": {
     "dependencies": ["permission:system.cpu"],
@@ -607,10 +610,10 @@
       "blessed_extension",
       "blessed_web_page",
       "content_script",
-      "extension_service_worker",
       "lock_screen_extension",
       "unblessed_extension"
-    ]
+    ],
+    "disallow_for_service_workers": false
   }, {
     "channel": "stable",
     "contexts": ["webui"],
@@ -640,12 +643,14 @@
   },
   "webRequest": {
     "dependencies": ["permission:webRequest"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   },
   "webRequestInternal": [{
     "internal": true,
     "channel": "stable",
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"],
+    "disallow_for_service_workers": false
   }, {
     // webview uses webRequestInternal API.
     "channel": "stable",
diff --git a/extensions/common/feature_switch.cc b/extensions/common/feature_switch.cc
index 20b2d99a..0109963 100644
--- a/extensions/common/feature_switch.cc
+++ b/extensions/common/feature_switch.cc
@@ -24,16 +24,13 @@
   CommonSwitches()
       : force_dev_mode_highlighting(switches::kForceDevModeHighlighting,
                                     FeatureSwitch::DEFAULT_DISABLED),
-        prompt_for_external_extensions(
-#if defined(CHROMIUM_BUILD)
-            switches::kPromptForExternalExtensions,
-#else
-            nullptr,
-#endif
+        // Intentionally no flag since turning this off outside of tests
+        // is a security risk.
+        prompt_for_external_extensions(nullptr,
 #if defined(OS_WIN) || defined(OS_MACOSX)
-            FeatureSwitch::DEFAULT_ENABLED),
+                                       FeatureSwitch::DEFAULT_ENABLED),
 #else
-            FeatureSwitch::DEFAULT_DISABLED),
+                                       FeatureSwitch::DEFAULT_DISABLED),
 #endif
         error_console(switches::kErrorConsole, FeatureSwitch::DEFAULT_ENABLED),
         enable_override_bookmarks_ui(switches::kEnableOverrideBookmarksUI,
diff --git a/extensions/common/features/feature.h b/extensions/common/features/feature.h
index 5301b34..b525131 100644
--- a/extensions/common/features/feature.h
+++ b/extensions/common/features/feature.h
@@ -38,7 +38,6 @@
     WEB_PAGE_CONTEXT,
     BLESSED_WEB_PAGE_CONTEXT,
     WEBUI_CONTEXT,
-    SERVICE_WORKER_CONTEXT,
     LOCK_SCREEN_EXTENSION_CONTEXT,
   };
 
diff --git a/extensions/common/features/manifest_feature.cc b/extensions/common/features/manifest_feature.cc
index 4d36d52..f5127d6 100644
--- a/extensions/common/features/manifest_feature.cc
+++ b/extensions/common/features/manifest_feature.cc
@@ -9,6 +9,9 @@
 namespace extensions {
 
 ManifestFeature::ManifestFeature() {
+  // TODO(crbug.com/979790): This will default to false once the transition
+  // to blocklisting unsupported APIs is complete.
+  set_disallow_for_service_workers(false);
 }
 
 ManifestFeature::~ManifestFeature() {
diff --git a/extensions/common/features/permission_feature.cc b/extensions/common/features/permission_feature.cc
index fe63041..5480830c 100644
--- a/extensions/common/features/permission_feature.cc
+++ b/extensions/common/features/permission_feature.cc
@@ -10,6 +10,9 @@
 namespace extensions {
 
 PermissionFeature::PermissionFeature() {
+  // TODO(crbug.com/979790): This will default to false once the transition
+  // to blocklisting unsupported APIs is complete.
+  set_disallow_for_service_workers(false);
 }
 
 PermissionFeature::~PermissionFeature() {
diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc
index 200db48..e911f5b 100644
--- a/extensions/common/features/simple_feature.cc
+++ b/extensions/common/features/simple_feature.cc
@@ -20,6 +20,7 @@
 #include "extensions/common/extension_api.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/features/feature_provider.h"
+#include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/switches.h"
 
 using crx_file::id_util::HashedIdInHex;
@@ -114,8 +115,6 @@
       return "hosted app";
     case Feature::WEBUI_CONTEXT:
       return "webui";
-    case Feature::SERVICE_WORKER_CONTEXT:
-      return "service worker";
     case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
       return "lock screen app";
   }
@@ -206,7 +205,11 @@
 }
 
 SimpleFeature::SimpleFeature()
-    : component_extensions_auto_granted_(true), is_internal_(false) {}
+    : component_extensions_auto_granted_(true),
+      is_internal_(false),
+      // TODO(crbug.com/979790): This will default to false once the transition
+      // to blocklisting unsupported APIs is complete.
+      disallow_for_service_workers_(true) {}
 
 SimpleFeature::~SimpleFeature() {}
 
@@ -248,7 +251,18 @@
       return manifest_availability;
   }
 
-  Availability context_availability = GetContextAvailability(context, url);
+  bool is_for_service_worker = false;
+  if (extension != nullptr && BackgroundInfo::IsServiceWorkerBased(extension) &&
+      url.is_valid()) {
+    const GURL script_url = extension->GetResourceURL(
+        BackgroundInfo::GetBackgroundServiceWorkerScript(extension));
+    if (script_url == url) {
+      is_for_service_worker = true;
+    }
+  }
+
+  Availability context_availability =
+      GetContextAvailability(context, url, is_for_service_worker);
   if (!context_availability.is_available())
     return context_availability;
 
@@ -616,7 +630,8 @@
 
 Feature::Availability SimpleFeature::GetContextAvailability(
     Feature::Context context,
-    const GURL& url) const {
+    const GURL& url,
+    bool is_for_service_worker) const {
   // TODO(lazyboy): This isn't quite right for Extension Service Worker
   // extension API calls, since there's no guarantee that the extension is
   // "active" in current renderer process when the API permission check is
@@ -632,6 +647,9 @@
     return CreateAvailability(INVALID_URL, url);
   }
 
+  if (is_for_service_worker && disallow_for_service_workers_)
+    return CreateAvailability(INVALID_CONTEXT);
+
   return CreateAvailability(IS_AVAILABLE);
 }
 
diff --git a/extensions/common/features/simple_feature.h b/extensions/common/features/simple_feature.h
index 7fc99fb..3d90326 100644
--- a/extensions/common/features/simple_feature.h
+++ b/extensions/common/features/simple_feature.h
@@ -108,6 +108,9 @@
   void set_extension_types(std::initializer_list<Manifest::Type> types);
   void set_session_types(std::initializer_list<FeatureSessionType> types);
   void set_internal(bool is_internal) { is_internal_ = is_internal; }
+  void set_disallow_for_service_workers(bool disallow) {
+    disallow_for_service_workers_ = disallow;
+  }
   void set_location(Location location) { location_ = location; }
   // set_matches() is an exception to pass-by-value since we construct an
   // URLPatternSet from the vector of strings.
@@ -211,7 +214,9 @@
                                        int manifest_version) const;
 
   // Returns the availability of the feature with respect to a given context.
-  Availability GetContextAvailability(Context context, const GURL& url) const;
+  Availability GetContextAvailability(Context context,
+                                      const GURL& url,
+                                      bool is_for_service_worker) const;
 
   // For clarity and consistency, we handle the default value of each of these
   // members the same way: it matches everything. It is up to the higher level
@@ -238,6 +243,7 @@
 
   bool component_extensions_auto_granted_;
   bool is_internal_;
+  bool disallow_for_service_workers_;
 
   DISALLOW_COPY_AND_ASSIGN(SimpleFeature);
 };
diff --git a/extensions/common/features/simple_feature_unittest.cc b/extensions/common/features/simple_feature_unittest.cc
index 7bc3b00..b56c8d44 100644
--- a/extensions/common/features/simple_feature_unittest.cc
+++ b/extensions/common/features/simple_feature_unittest.cc
@@ -16,10 +16,12 @@
 #include "base/test/scoped_command_line.h"
 #include "base/values.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/common/extension_builder.h"
 #include "extensions/common/features/complex_feature.h"
 #include "extensions/common/features/feature_channel.h"
 #include "extensions/common/features/feature_session_type.h"
 #include "extensions/common/manifest.h"
+#include "extensions/common/manifest_handlers/background_info.h"
 #include "extensions/common/switches.h"
 #include "extensions/common/value_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -988,4 +990,55 @@
   }
 }
 
+TEST(SimpleFeatureUnitTest, DisallowForServiceWorkers) {
+  // Service Worker features are only available on the trunk.
+  ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN);
+
+  SimpleFeature feature;
+  feature.set_name("somefeature");
+  feature.set_contexts({Feature::BLESSED_EXTENSION_CONTEXT});
+  feature.set_extension_types({Manifest::TYPE_EXTENSION});
+
+  constexpr char script_file[] = "script.js";
+
+  auto extension =
+      ExtensionBuilder("test")
+          .SetManifestPath({"background", "service_worker"}, script_file)
+          .Build();
+  ASSERT_TRUE(extension.get());
+  EXPECT_TRUE(BackgroundInfo::IsServiceWorkerBased(extension.get()));
+
+  // Expect the feature is not allowed, since the initial state is disallowed.
+  // TODO(crbug.com/979790): This will default to allowed once the transition
+  // to blocklisting unsupported APIs is complete. This will require swapping
+  // these two EXPECTs.
+  EXPECT_EQ(Feature::INVALID_CONTEXT,
+            feature
+                .IsAvailableToContext(extension.get(),
+                                      Feature::BLESSED_EXTENSION_CONTEXT,
+                                      extension->GetResourceURL(script_file),
+                                      Feature::CHROMEOS_PLATFORM)
+                .result());
+
+  // Check with a different script file, which should return available,
+  // since it's not a service worker context.
+  EXPECT_EQ(Feature::IS_AVAILABLE,
+            feature
+                .IsAvailableToContext(extension.get(),
+                                      Feature::BLESSED_EXTENSION_CONTEXT,
+                                      extension->GetResourceURL("other.js"),
+                                      Feature::CHROMEOS_PLATFORM)
+                .result());
+
+  // Enable the feature for service workers.
+  feature.set_disallow_for_service_workers(false);
+  EXPECT_EQ(Feature::IS_AVAILABLE,
+            feature
+                .IsAvailableToContext(extension.get(),
+                                      Feature::BLESSED_EXTENSION_CONTEXT,
+                                      extension->GetResourceURL(script_file),
+                                      Feature::CHROMEOS_PLATFORM)
+                .result());
+}
+
 }  // namespace extensions
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc
index fa7f9ad9..e9b647bf9 100644
--- a/extensions/common/switches.cc
+++ b/extensions/common/switches.cc
@@ -66,12 +66,6 @@
 // Comma-separated list of paths to extensions to load at startup.
 const char kLoadExtension[] = "load-extension";
 
-#if defined(CHROMIUM_BUILD)
-// Should we prompt the user before allowing external extensions to install?
-// This flag is available on Chromium for testing purposes.
-const char kPromptForExternalExtensions[] = "prompt-for-external-extensions";
-#endif
-
 // Set the parameters for ExtensionURLLoaderThrottleBrowserTest.
 const char kSetExtensionThrottleTestParams[] =
     "set-extension-throttle-test-params";
diff --git a/extensions/common/switches.h b/extensions/common/switches.h
index 96161b1..f5593d2 100644
--- a/extensions/common/switches.h
+++ b/extensions/common/switches.h
@@ -26,9 +26,6 @@
 extern const char kForceEmptyCorbAllowlist[];
 extern const char kLoadApps[];
 extern const char kLoadExtension[];
-#if defined(CHROMIUM_BUILD)
-extern const char kPromptForExternalExtensions[];
-#endif
 extern const char kSetExtensionThrottleTestParams[];
 extern const char kShowComponentExtensionOptions[];
 extern const char kTraceAppSource[];
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 16a1a00..d32e3060 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -323,6 +323,9 @@
                           elapsed);
       break;
     case Feature::BLESSED_EXTENSION_CONTEXT:
+      // For service workers this is handled in
+      // DidInitializeServiceWorkerContextOnWorkerThread().
+      DCHECK(!context->IsForServiceWorker());
       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_Blessed", elapsed);
       break;
     case Feature::UNBLESSED_EXTENSION_CONTEXT:
@@ -343,10 +346,6 @@
     case Feature::WEBUI_CONTEXT:
       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_WebUI", elapsed);
       break;
-    case Feature::SERVICE_WORKER_CONTEXT:
-      // Handled in DidInitializeServiceWorkerContextOnWorkerThread().
-      NOTREACHED();
-      break;
     case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
       UMA_HISTOGRAM_TIMES(
           "Extensions.DidCreateScriptContext_LockScreenExtension", elapsed);
@@ -407,8 +406,8 @@
   // by extensions minimal bindings, just as we might want to give them to
   // service workers that aren't registered by extensions.
   ScriptContext* context = new ScriptContext(
-      v8_context, nullptr, extension, Feature::SERVICE_WORKER_CONTEXT,
-      extension, Feature::SERVICE_WORKER_CONTEXT);
+      v8_context, nullptr, extension, Feature::BLESSED_EXTENSION_CONTEXT,
+      extension, Feature::BLESSED_EXTENSION_CONTEXT);
   context->set_url(script_url);
   context->set_service_worker_scope(service_worker_scope);
   context->set_service_worker_version_id(service_worker_version_id);
@@ -1347,7 +1346,6 @@
 }
 
 void Dispatcher::RequireGuestViewModules(ScriptContext* context) {
-  Feature::Context context_type = context->context_type();
   ModuleSystem* module_system = context->module_system();
   bool requires_guest_view_module = false;
 
@@ -1402,7 +1400,8 @@
   // error-providing custom elements for the GuestView types that are not
   // available, and thus all of those types must have been checked and loaded
   // (or not loaded) beforehand.
-  if (context_type == Feature::BLESSED_EXTENSION_CONTEXT) {
+  if (context->context_type() == Feature::BLESSED_EXTENSION_CONTEXT &&
+      !context->IsForServiceWorker()) {
     module_system->Require("guestViewDeny");
   }
 }
diff --git a/extensions/renderer/ipc_message_sender.cc b/extensions/renderer/ipc_message_sender.cc
index 6d2fd75..d8938d6b 100644
--- a/extensions/renderer/ipc_message_sender.cc
+++ b/extensions/renderer/ipc_message_sender.cc
@@ -84,7 +84,7 @@
   void SendAddUnfilteredEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_AddListener(
@@ -95,7 +95,7 @@
   void SendRemoveUnfilteredEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_RemoveListener(
@@ -106,7 +106,7 @@
   void SendAddUnfilteredLazyEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_AddLazyListener(
@@ -116,7 +116,7 @@
   void SendRemoveUnfilteredLazyEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_RemoveLazyListener(
@@ -127,7 +127,7 @@
                                        const std::string& event_name,
                                        const base::DictionaryValue& filter,
                                        bool is_lazy) override {
-    DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_AddFilteredListener(
@@ -138,7 +138,7 @@
                                           const std::string& event_name,
                                           const base::DictionaryValue& filter,
                                           bool remove_lazy_listener) override {
-    DCHECK_NE(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_RemoveFilteredListener(
@@ -229,7 +229,7 @@
                       std::unique_ptr<ExtensionHostMsg_Request_Params> params,
                       binding::RequestThread thread) override {
     DCHECK(!context->GetRenderFrame());
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     int worker_thread_id = content::WorkerThread::GetCurrentId();
@@ -266,7 +266,7 @@
   void SendAddUnfilteredEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
     DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
               context->service_worker_version_id());
@@ -280,7 +280,7 @@
   void SendRemoveUnfilteredEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
     DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
               context->service_worker_version_id());
@@ -294,7 +294,7 @@
   void SendAddUnfilteredLazyEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     dispatcher_->Send(new ExtensionHostMsg_AddLazyServiceWorkerListener(
@@ -305,7 +305,7 @@
   void SendRemoveUnfilteredLazyEventListenerIPC(
       ScriptContext* context,
       const std::string& event_name) override {
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     dispatcher_->Send(new ExtensionHostMsg_RemoveLazyServiceWorkerListener(
@@ -317,7 +317,7 @@
                                        const std::string& event_name,
                                        const base::DictionaryValue& filter,
                                        bool is_lazy) override {
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
     DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
               context->service_worker_version_id());
@@ -333,7 +333,7 @@
                                           const std::string& event_name,
                                           const base::DictionaryValue& filter,
                                           bool remove_lazy_listener) override {
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
+    DCHECK(context->IsForServiceWorker());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
     DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
               context->service_worker_version_id());
@@ -352,7 +352,7 @@
                               const std::string& channel_name,
                               bool include_tls_channel_id) override {
     DCHECK(!script_context->GetRenderFrame());
-    DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, script_context->context_type());
+    DCHECK(script_context->IsForServiceWorker());
     const Extension* extension = script_context->extension();
 
     switch (target.type) {
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc
index 2e8e06629..a6224cc 100644
--- a/extensions/renderer/module_system.cc
+++ b/extensions/renderer/module_system.cc
@@ -182,7 +182,7 @@
 
   if (context_->GetRenderFrame() &&
       context_->context_type() == Feature::BLESSED_EXTENSION_CONTEXT &&
-      ContextNeedsMojoBindings(context_)) {
+      !context_->IsForServiceWorker() && ContextNeedsMojoBindings(context_)) {
     blink::WebContextFeatures::EnableMojoJS(context->v8_context(), true);
   }
 }
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc
index 44da658..83a5649 100644
--- a/extensions/renderer/native_extension_bindings_system.cc
+++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -359,6 +359,7 @@
 // Logs the amount of time taken to update the bindings for a given context
 // (i.e., UpdateBindingsForContext()).
 void LogUpdateBindingsForContextTime(Feature::Context context_type,
+                                     bool is_for_service_worker,
                                      base::TimeDelta elapsed) {
   constexpr int kHistogramBucketCount = 50;
   static const int kTenSecondsInMicroseconds = 10000000;
@@ -378,19 +379,20 @@
           elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
           kHistogramBucketCount);
       break;
-    case Feature::SERVICE_WORKER_CONTEXT:
-      UMA_HISTOGRAM_CUSTOM_COUNTS(
-          "Extensions.Bindings.UpdateBindingsForContextTime."
-          "ServiceWorkerContext",
-          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
-          kHistogramBucketCount);
-      break;
     case Feature::BLESSED_EXTENSION_CONTEXT:
-      UMA_HISTOGRAM_CUSTOM_COUNTS(
-          "Extensions.Bindings.UpdateBindingsForContextTime."
-          "BlessedExtensionContext",
-          elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
-          kHistogramBucketCount);
+      if (is_for_service_worker) {
+        UMA_HISTOGRAM_CUSTOM_COUNTS(
+            "Extensions.Bindings.UpdateBindingsForContextTime."
+            "ServiceWorkerContext",
+            elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+            kHistogramBucketCount);
+      } else {
+        UMA_HISTOGRAM_CUSTOM_COUNTS(
+            "Extensions.Bindings.UpdateBindingsForContextTime."
+            "BlessedExtensionContext",
+            elapsed.InMicroseconds(), 1, kTenSecondsInMicroseconds,
+            kHistogramBucketCount);
+      }
       break;
     case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
       UMA_HISTOGRAM_CUSTOM_COUNTS(
@@ -541,11 +543,11 @@
     case Feature::BLESSED_WEB_PAGE_CONTEXT:
       is_webpage = true;
       break;
-    case Feature::SERVICE_WORKER_CONTEXT:
-      DCHECK(ExtensionsClient::Get()
-                 ->ExtensionAPIEnabledInExtensionServiceWorkers());
-      FALLTHROUGH;
     case Feature::BLESSED_EXTENSION_CONTEXT:
+      if (context->IsForServiceWorker())
+        DCHECK(ExtensionsClient::Get()
+                   ->ExtensionAPIEnabledInExtensionServiceWorkers());
+      FALLTHROUGH;
     case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
     case Feature::UNBLESSED_EXTENSION_CONTEXT:
     case Feature::CONTENT_SCRIPT_CONTEXT:
@@ -573,7 +575,9 @@
     if (IsRuntimeAvailableToContext(context) && !set_accessor("runtime"))
       LOG(ERROR) << "Failed to create API on Chrome object.";
 
-    LogUpdateBindingsForContextTime(context->context_type(), timer.Elapsed());
+    LogUpdateBindingsForContextTime(context->context_type(),
+                                    context->IsForServiceWorker(),
+                                    timer.Elapsed());
     return;
   }
 
@@ -606,7 +610,8 @@
     }
   }
 
-  LogUpdateBindingsForContextTime(context->context_type(), timer.Elapsed());
+  LogUpdateBindingsForContextTime(
+      context->context_type(), context->IsForServiceWorker(), timer.Elapsed());
 }
 
 void NativeExtensionBindingsSystem::DispatchEventInContext(
@@ -857,10 +862,9 @@
   // manually by the extension and the context is a lazy context.
   // Note: Check context_type() first to avoid accessing ExtensionFrameHelper on
   // a worker thread.
-  bool is_lazy =
-      update_lazy_listeners &&
-      (script_context->context_type() == Feature::SERVICE_WORKER_CONTEXT ||
-       ExtensionFrameHelper::IsContextForEventPage(script_context));
+  bool is_lazy = update_lazy_listeners &&
+                 (script_context->IsForServiceWorker() ||
+                  ExtensionFrameHelper::IsContextForEventPage(script_context));
 
   switch (change) {
     case binding::EventListenersChanged::
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index cfb1d90..96ab855 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -49,8 +49,6 @@
       return "BLESSED_WEB_PAGE";
     case Feature::WEBUI_CONTEXT:
       return "WEBUI";
-    case Feature::SERVICE_WORKER_CONTEXT:
-      return "SERVICE_WORKER";
     case Feature::LOCK_SCREEN_EXTENSION_CONTEXT:
       return "LOCK_SCREEN_EXTENSION";
   }
@@ -268,15 +266,20 @@
 }
 
 const GURL& ScriptContext::service_worker_scope() const {
-  DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context_type());
+  DCHECK(IsForServiceWorker());
   return service_worker_scope_;
 }
 
+bool ScriptContext::IsForServiceWorker() const {
+  return service_worker_version_id_ !=
+         blink::mojom::kInvalidServiceWorkerVersionId;
+}
+
 bool ScriptContext::IsAnyFeatureAvailableToContext(
     const Feature& api,
     CheckAliasStatus check_alias) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  // TODO(lazyboy): Decide what we should do for SERVICE_WORKER_CONTEXT, where
+  // TODO(lazyboy): Decide what we should do for service workers, where
   // web_frame() is null.
   GURL url = web_frame() ? GetDocumentLoaderURLForFrame(web_frame()) : url_;
   return ExtensionAPI::GetSharedInstance()->IsAnyFeatureAvailableToContext(
diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h
index e6d98085..dee5e55e 100644
--- a/extensions/renderer/script_context.h
+++ b/extensions/renderer/script_context.h
@@ -156,6 +156,8 @@
     return service_worker_version_id_;
   }
 
+  bool IsForServiceWorker() const;
+
   // Sets the URL of this ScriptContext. Usually this will automatically be set
   // on construction, unless this isn't constructed with enough information to
   // determine the URL (e.g. frame was null).
diff --git a/fuchsia/engine/context_provider_main.cc b/fuchsia/engine/context_provider_main.cc
index 9d2c532..5355a81 100644
--- a/fuchsia/engine/context_provider_main.cc
+++ b/fuchsia/engine/context_provider_main.cc
@@ -23,7 +23,7 @@
       directory, &context_provider);
 
   base::fuchsia::ScopedServiceBinding<fuchsia::web::Debug> debug_binding(
-      directory->debug(), &context_provider);
+      directory->outgoing_directory()->debug_dir(), &context_provider);
 
   base::RunLoop run_loop;
   cr_fuchsia::LifecycleImpl lifecycle(directory, run_loop.QuitClosure());
diff --git a/gpu/config/gpu_info.cc b/gpu/config/gpu_info.cc
index 7b9fddb..b7ef6c9 100644
--- a/gpu/config/gpu_info.cc
+++ b/gpu/config/gpu_info.cc
@@ -55,9 +55,13 @@
   switch (type) {
     case gpu::ImageDecodeAcceleratorType::kJpeg:
       return "JPEG";
+    case gpu::ImageDecodeAcceleratorType::kWebP:
+      return "WebP";
     case gpu::ImageDecodeAcceleratorType::kUnknown:
       return "Unknown";
   }
+  NOTREACHED() << "Invalid ImageDecodeAcceleratorType.";
+  return "";
 }
 
 const char* ImageDecodeAcceleratorSubsamplingToString(
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h
index 0e35bc60..a6c025bb 100644
--- a/gpu/config/gpu_info.h
+++ b/gpu/config/gpu_info.h
@@ -116,9 +116,10 @@
     std::vector<VideoEncodeAcceleratorSupportedProfile>;
 
 enum class ImageDecodeAcceleratorType {
-  kJpeg = 0,
-  kUnknown = 1,
-  kMaxValue = kUnknown,
+  kUnknown = 0,
+  kJpeg = 1,
+  kWebP = 2,
+  kMaxValue = kWebP,
 };
 
 enum class ImageDecodeAcceleratorSubsampling {
diff --git a/gpu/ipc/client/DEPS b/gpu/ipc/client/DEPS
index 88b89ed..b6aef8d 100644
--- a/gpu/ipc/client/DEPS
+++ b/gpu/ipc/client/DEPS
@@ -12,6 +12,8 @@
   ],
   "image_decode_accelerator_proxy.cc": [
     "+media/parsers/jpeg_parser.h",
+    "+media/parsers/vp8_parser.h",
+    "+media/parsers/webp_parser.h",
   ],
   "raster_in_process_context_tests.cc": [
     "+components/viz/common/resources/resource_format.h",
diff --git a/gpu/ipc/client/image_decode_accelerator_proxy.cc b/gpu/ipc/client/image_decode_accelerator_proxy.cc
index 098a2cf2..7765f6ef 100644
--- a/gpu/ipc/client/image_decode_accelerator_proxy.cc
+++ b/gpu/ipc/client/image_decode_accelerator_proxy.cc
@@ -7,6 +7,7 @@
 #include <string.h>
 
 #include <algorithm>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -18,6 +19,8 @@
 #include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "media/parsers/jpeg_parser.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/parsers/webp_parser.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
 
@@ -33,14 +36,16 @@
 
 ImageDecodeAcceleratorType GetImageType(
     base::span<const uint8_t> encoded_data) {
-  static_assert(static_cast<int>(ImageDecodeAcceleratorType::kMaxValue) == 1,
+  static_assert(static_cast<int>(ImageDecodeAcceleratorType::kMaxValue) == 2,
                 "GetImageType() must be adapted to support all image types in "
                 "ImageDecodeAcceleratorType");
 
-  // Currently, only JPEG images are supported.
   if (IsJpegImage(encoded_data))
     return ImageDecodeAcceleratorType::kJpeg;
 
+  if (media::IsLossyWebPImage(encoded_data))
+    return ImageDecodeAcceleratorType::kWebP;
+
   return ImageDecodeAcceleratorType::kUnknown;
 }
 
@@ -124,6 +129,27 @@
   return true;
 }
 
+bool IsSupportedWebPImage(
+    base::span<const uint8_t> encoded_data,
+    const ImageDecodeAcceleratorSupportedProfile& supported_profile) {
+  DCHECK(media::IsLossyWebPImage(encoded_data));
+  DCHECK_EQ(ImageDecodeAcceleratorType::kWebP, supported_profile.image_type);
+
+  const std::unique_ptr<media::Vp8FrameHeader> parse_result =
+      media::ParseWebPImage(encoded_data);
+  if (!parse_result)
+    return false;
+
+  // TODO(crbug.com/984971): we may need to compute the coded size instead and
+  // check that against the supported dimensions.
+  const int width = base::strict_cast<int>(parse_result->width);
+  const int height = base::strict_cast<int>(parse_result->height);
+  return width >= supported_profile.min_encoded_dimensions.width() &&
+         height >= supported_profile.min_encoded_dimensions.height() &&
+         width <= supported_profile.max_encoded_dimensions.width() &&
+         height <= supported_profile.max_encoded_dimensions.height();
+}
+
 }  // namespace
 
 ImageDecodeAcceleratorProxy::ImageDecodeAcceleratorProxy(GpuChannelHost* host,
@@ -153,10 +179,15 @@
     return false;
 
   // Validate the image according to that profile.
-  if (image_type == ImageDecodeAcceleratorType::kJpeg)
-    return IsSupportedJpegImage(encoded_data, *profile_it);
-
-  NOTREACHED();
+  switch (image_type) {
+    case ImageDecodeAcceleratorType::kJpeg:
+      return IsSupportedJpegImage(encoded_data, *profile_it);
+    case ImageDecodeAcceleratorType::kWebP:
+      return IsSupportedWebPImage(encoded_data, *profile_it);
+    case ImageDecodeAcceleratorType::kUnknown:
+      // No Op. Should not reach due to a check above.
+      break;
+  }
   return false;
 }
 
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom
index 9705ef1..9739786b 100644
--- a/gpu/ipc/common/gpu_info.mojom
+++ b/gpu/ipc/common/gpu_info.mojom
@@ -81,6 +81,7 @@
 // gpu::ImageDecodeAcceleratorType
 enum ImageDecodeAcceleratorType {
   kJpeg,
+  kWebP,
   kUnknown,
 };
 
diff --git a/gpu/ipc/common/gpu_info_struct_traits.cc b/gpu/ipc/common/gpu_info_struct_traits.cc
index 28d5662..e88aa9f 100644
--- a/gpu/ipc/common/gpu_info_struct_traits.cc
+++ b/gpu/ipc/common/gpu_info_struct_traits.cc
@@ -237,6 +237,8 @@
   switch (image_type) {
     case gpu::ImageDecodeAcceleratorType::kJpeg:
       return gpu::mojom::ImageDecodeAcceleratorType::kJpeg;
+    case gpu::ImageDecodeAcceleratorType::kWebP:
+      return gpu::mojom::ImageDecodeAcceleratorType::kWebP;
     case gpu::ImageDecodeAcceleratorType::kUnknown:
       return gpu::mojom::ImageDecodeAcceleratorType::kUnknown;
   }
@@ -251,6 +253,9 @@
     case gpu::mojom::ImageDecodeAcceleratorType::kJpeg:
       *out = gpu::ImageDecodeAcceleratorType::kJpeg;
       return true;
+    case gpu::mojom::ImageDecodeAcceleratorType::kWebP:
+      *out = gpu::ImageDecodeAcceleratorType::kWebP;
+      return true;
     case gpu::mojom::ImageDecodeAcceleratorType::kUnknown:
       *out = gpu::ImageDecodeAcceleratorType::kUnknown;
       return true;
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 9825f6fb..eec0cfb 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -3729,6 +3729,12 @@
     }
     builders {
       mixins: "android-try"
+      name: "android-oreo-arm64-rel"
+      mixins: "linux-xenial"
+      mixins: "builderless"
+    }
+    builders {
+      mixins: "android-try"
       mixins: "goma-j300"
       name: "android_arm64_dbg_recipe"
       mixins: "linux-xenial"
@@ -3823,29 +3829,29 @@
     builders {
       mixins: "android-optional-gpu-try"
       mixins: "goma-rbe-prod"
-      name: "gpu-fyi-try-android-p-pixel-2-32-vk"
-    }
-    builders {
-      mixins: "android-optional-gpu-try"
-      mixins: "goma-rbe-prod"
-      name: "gpu-fyi-try-android-p-pixel-2-32-deqp-vk"
-    }
-    builders {
-      mixins: "android-optional-gpu-try"
-      mixins: "goma-rbe-prod"
-      name: "gpu-fyi-try-android-p-pixel-2-64-vk"
-    }
-    builders {
-      mixins: "android-optional-gpu-try"
-      mixins: "goma-rbe-prod"
-      name: "gpu-fyi-try-android-p-pixel-2-64-deqp-vk"
-    }
-    builders {
-      mixins: "android-optional-gpu-try"
-      mixins: "goma-rbe-prod"
       name: "gpu-fyi-try-android-p-pixel-2-skv-32"
     }
     builders {
+      mixins: "android-optional-gpu-try"
+      mixins: "goma-rbe-prod"
+      name: "gpu-fyi-try-android-q-pixel-2-deqp-vk-32"
+    }
+    builders {
+      mixins: "android-optional-gpu-try"
+      mixins: "goma-rbe-prod"
+      name: "gpu-fyi-try-android-q-pixel-2-deqp-vk-64"
+    }
+    builders {
+      mixins: "android-optional-gpu-try"
+      mixins: "goma-rbe-prod"
+      name: "gpu-fyi-try-android-q-pixel-2-vk-32"
+    }
+    builders {
+      mixins: "android-optional-gpu-try"
+      mixins: "goma-rbe-prod"
+      name: "gpu-fyi-try-android-q-pixel-2-vk-64"
+    }
+    builders {
       mixins: "android-try"
       mixins: "deterministic"
       mixins: "goma-rbe-prod"
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 3ae0d19..aebc5cb9 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -4056,6 +4056,9 @@
     name: "buildbucket/luci.chromium.try/android-marshmallow-arm64-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-oreo-arm64-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android_optional_gpu_tests_rel"
   }
   builders {
@@ -4092,21 +4095,21 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-32"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-32-vk"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-32-deqp-vk"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-64-vk"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-64-deqp-vk"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-skv-32"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-deqp-vk-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-deqp-vk-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-vk-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-vk-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android-deterministic-rel"
   }
   builders {
@@ -4684,6 +4687,9 @@
     name: "buildbucket/luci.chromium.try/android-oreo-arm64-cts-networkservice-dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/android-oreo-arm64-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/android_archive_rel_ng"
   }
   builders {
@@ -4777,21 +4783,21 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-32"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-32-vk"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-32-deqp-vk"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-64-vk"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-64-deqp-vk"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-p-pixel-2-skv-32"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-deqp-vk-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-deqp-vk-64"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-vk-32"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-q-pixel-2-vk-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-dqp"
   }
   builders {
diff --git a/ios/build/bots/chromium.clang/ToTiOSDevice.json b/ios/build/bots/chromium.clang/ToTiOSDevice.json
index 78a5a90..388192a 100644
--- a/ios/build/bots/chromium.clang/ToTiOSDevice.json
+++ b/ios/build/bots/chromium.clang/ToTiOSDevice.json
@@ -37,103 +37,103 @@
     {
       "app": "base_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "boringssl_crypto_tests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "boringssl_ssl_tests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "components_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "crypto_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "gfx_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "google_apis_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "ios_chrome_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "ios_net_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "ios_web_inttests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "ios_web_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "ios_web_view_inttests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "net_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "skia_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "sql_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "ui_base_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     },
     {
       "app": "url_unittests",
       "device type": "iPhone 6s",
-      "xcode build version": "10e1001",
+      "xcode build version": "11m374r",
       "os": "12.3.1"
     }
   ],
diff --git a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
index 209d6b3..36c3ba5 100644
--- a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
@@ -74,34 +74,6 @@
       }
     },
     {
-      "xcode parallelization": true,
-      "include": "eg2_tests.json",
-      "device type": "iPhone X",
-      "os": "11.4",
-      "xcode build version": "10e1001",
-      "pool":"Chrome",
-      "host os": "Mac-10.14.4",
-      "optional_dimensions": {
-          "60": [{
-            "host os": "Mac-10.14.5"
-          }]
-      }
-    },
-    {
-      "xcode parallelization": true,
-      "include": "eg2_tests.json",
-      "device type": "iPad (6th generation)",
-      "os": "11.4",
-      "xcode build version": "10e1001",
-      "pool":"Chrome",
-      "host os": "Mac-10.14.4",
-      "optional_dimensions": {
-          "60": [{
-            "host os": "Mac-10.14.5"
-          }]
-      }
-    },
-    {
       "include": "common_tests.json",
       "device type": "iPhone X",
       "os": "13.0",
@@ -115,7 +87,7 @@
       }
     },
     {
-      "xcode parallelization": false,
+      "shards": 1,
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
       "os": "13.0",
@@ -129,7 +101,7 @@
       }
     },
     {
-      "xcode parallelization": false,
+      "shards": 1,
       "include": "eg_cq_tests.json",
       "device type": "iPad Pro (12.9-inch)",
       "os": "13.0",
@@ -143,7 +115,7 @@
       }
     },
     {
-      "xcode parallelization": false,
+      "shards": 1,
       "include": "eg_tests.json",
       "device type": "iPhone X",
       "os": "13.0",
@@ -157,7 +129,7 @@
       }
     },
     {
-      "xcode parallelization": false,
+      "shards": 1,
       "include": "eg_tests.json",
       "device type": "iPad Air (3rd generation)",
       "os": "13.0",
diff --git a/ios/build/bots/chromium.mac/ios13-beta-simulator.json b/ios/build/bots/chromium.mac/ios13-beta-simulator.json
index df471c9..6741bb6 100644
--- a/ios/build/bots/chromium.mac/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.mac/ios13-beta-simulator.json
@@ -76,34 +76,6 @@
       }
     },
     {
-      "xcode parallelization": true,
-      "include": "eg2_tests.json",
-      "device type": "iPhone X",
-      "os": "11.4",
-      "xcode build version": "10e1001",
-      "pool":"Chrome",
-      "host os": "Mac-10.14.4",
-      "optional_dimensions": {
-          "60": [{
-            "host os": "Mac-10.14.5"
-          }]
-      }
-    },
-    {
-      "xcode parallelization": true,
-      "include": "eg2_tests.json",
-      "device type": "iPad (6th generation)",
-      "os": "11.4",
-      "xcode build version": "10e1001",
-      "pool":"Chrome",
-      "host os": "Mac-10.14.4",
-      "optional_dimensions": {
-          "60": [{
-            "host os": "Mac-10.14.5"
-          }]
-      }
-    },
-    {
       "include": "common_tests.json",
       "device type": "iPhone X",
       "os": "13.0",
diff --git a/ios/build/bots/scripts/run.py b/ios/build/bots/scripts/run.py
index 6bd23d8..50dea89 100755
--- a/ios/build/bots/scripts/run.py
+++ b/ios/build/bots/scripts/run.py
@@ -290,6 +290,7 @@
   args.xctest = args_json.get('xctest', args.xctest)
   args.xcode_parallelization = args_json.get('xcode_parallelization',
                                              args.xcode_parallelization)
+  args.shards = args_json.get('shards', args.shards)
   test_args.extend(args_json.get('test_args', []))
 
   return args, test_args
diff --git a/ios/chrome/browser/browser_state/browser_state_services_egtest.mm b/ios/chrome/browser/browser_state/browser_state_services_egtest.mm
index ac82feb..62f3c206 100644
--- a/ios/chrome/browser/browser_state/browser_state_services_egtest.mm
+++ b/ios/chrome/browser/browser_state/browser_state_services_egtest.mm
@@ -11,7 +11,7 @@
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #include "ios/web/public/browser_state.h"
-#include "ios/web/public/service_manager_connection.h"
+#include "ios/web/public/service/service_manager_connection.h"
 #include "services/identity/public/mojom/constants.mojom.h"
 #include "services/identity/public/mojom/identity_accessor.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
diff --git a/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc b/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
index 5913ca7f..a1fbb1c 100644
--- a/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
+++ b/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
@@ -25,7 +25,7 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/google/google_brand.h"
 #include "ios/chrome/common/channel_info.h"
-#include "ios/web/public/service_manager_connection.h"
+#include "ios/web/public/service/service_manager_connection.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
 
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index 8a5bb10..6056bdf 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -51,6 +51,7 @@
     "//ios/chrome/browser/ui/fullscreen:feature_flags",
     "//ios/chrome/browser/ui/infobars:feature_flags",
     "//ios/chrome/browser/ui/settings/autofill:feature_flags",
+    "//ios/chrome/browser/ui/table_view:feature_flags",
     "//ios/chrome/browser/ui/toolbar/public:feature_flags",
     "//ios/chrome/browser/ui/toolbar_container:feature_flags",
     "//ios/chrome/browser/web:feature_flags",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 51faa1aa..3065c7e 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -62,6 +62,7 @@
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "ios/chrome/browser/ui/settings/autofill/features.h"
+#import "ios/chrome/browser/ui/table_view/feature_flags.h"
 #import "ios/chrome/browser/ui/toolbar/public/features.h"
 #import "ios/chrome/browser/ui/toolbar_container/toolbar_container_features.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -578,6 +579,10 @@
      flag_descriptions::kSettingsAddPaymentMethodName,
      flag_descriptions::kSettingsAddPaymentMethodDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kSettingsAddPaymentMethod)},
+    {"collections-card-presentation-style",
+     flag_descriptions::kCollectionsCardPresentationStyleName,
+     flag_descriptions::kCollectionsCardPresentationStyleDescription,
+     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kCollectionsCardPresentationStyle)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 7e3bc48..18b0b5f8 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -126,6 +126,12 @@
     "Automatically switches to the regular tabs panel in the tab grid after "
     "closing the last incognito tab";
 
+const char kCollectionsCardPresentationStyleName[] =
+    "Card style presentation for Collections.";
+const char kCollectionsCardPresentationStyleDescription[] =
+    "When enabled collections are presented using the new iOS13 card "
+    "style.";
+
 const char kContextualSearch[] = "Contextual Search";
 const char kContextualSearchDescription[] =
     "Whether or not Contextual Search is enabled.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index bfc664a4..b6bbc04 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -97,6 +97,11 @@
 extern const char kClosingLastIncognitoTabName[];
 extern const char kClosingLastIncognitoTabDescription[];
 
+// Title and description for the flag that controls whether Collections are
+// presented using the new iOS13 Card style or the custom legacy one.
+extern const char kCollectionsCardPresentationStyleName[];
+extern const char kCollectionsCardPresentationStyleDescription[];
+
 // Title and description for the flag to enable Contextual Search.
 extern const char kContextualSearch[];
 extern const char kContextualSearchDescription[];
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 1e7ef8c..fcae8b74 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -39,6 +39,7 @@
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/strings/grit/components_locale_settings.h"
 #include "components/sync/base/sync_prefs.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_sessions/session_sync_prefs.h"
 #include "components/translate/core/browser/translate_pref_names.h"
 #include "components/translate/core/browser/translate_prefs.h"
@@ -131,6 +132,7 @@
   PrefProxyConfigTrackerImpl::RegisterProfilePrefs(registry);
   RegisterVoiceSearchBrowserStatePrefs(registry);
   sync_sessions::SessionSyncPrefs::RegisterProfilePrefs(registry);
+  syncer::DeviceInfoPrefs::RegisterProfilePrefs(registry);
   syncer::SyncPrefs::RegisterProfilePrefs(registry);
   syncer::PerUserTopicRegistrationManager::RegisterProfilePrefs(registry);
   syncer::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry);
diff --git a/ios/chrome/browser/sync/device_info_sync_service_factory.mm b/ios/chrome/browser/sync/device_info_sync_service_factory.mm
index a254733..df61478f 100644
--- a/ios/chrome/browser/sync/device_info_sync_service_factory.mm
+++ b/ios/chrome/browser/sync/device_info_sync_service_factory.mm
@@ -13,6 +13,7 @@
 #include "components/send_tab_to_self/features.h"
 #include "components/signin/public/base/device_id_helper.h"
 #include "components/sync/model/model_type_store_service.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_device_info/device_info_sync_service_impl.h"
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 #include "ios/chrome/browser/application_context.h"
@@ -83,9 +84,11 @@
           base::BindRepeating(
               &send_tab_to_self::IsReceivingEnabledByUserOnThisDevice,
               browser_state->GetPrefs()));
+  auto device_prefs =
+      std::make_unique<syncer::DeviceInfoPrefs>(browser_state->GetPrefs());
 
   return std::make_unique<syncer::DeviceInfoSyncServiceImpl>(
       ModelTypeStoreServiceFactory::GetForBrowserState(browser_state)
           ->GetStoreFactory(),
-      std::move(local_device_info_provider));
+      std::move(local_device_info_provider), std::move(device_prefs));
 }
diff --git a/ios/chrome/browser/ui/table_view/BUILD.gn b/ios/chrome/browser/ui/table_view/BUILD.gn
index e6471f21..039d32e 100644
--- a/ios/chrome/browser/ui/table_view/BUILD.gn
+++ b/ios/chrome/browser/ui/table_view/BUILD.gn
@@ -79,6 +79,17 @@
   ]
 }
 
+source_set("feature_flags") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "feature_flags.h",
+    "feature_flags.mm",
+  ]
+  deps = [
+    "//base",
+  ]
+}
+
 source_set("test_support") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/table_view/feature_flags.h b/ios/chrome/browser/ui/table_view/feature_flags.h
new file mode 100644
index 0000000..67d1ed3d
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/feature_flags.h
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_TABLE_VIEW_FEATURE_FLAGS_H_
+#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_FEATURE_FLAGS_H_
+
+#include "base/feature_list.h"
+
+// Feature to choose whether to use the new Card iOS13 presentation, or the
+// legacy one.
+extern const base::Feature kCollectionsCardPresentationStyle;
+
+// Whether the kCollectionsCardPresentationStyle flag is enabled.
+bool IsCollectionsCardPresentationStyleEnabled();
+
+#endif  // IOS_CHROME_BROWSER_UI_TABLE_VIEW_FEATURE_FLAGS_H_
diff --git a/ios/chrome/browser/ui/table_view/feature_flags.mm b/ios/chrome/browser/ui/table_view/feature_flags.mm
new file mode 100644
index 0000000..9f18f73b
--- /dev/null
+++ b/ios/chrome/browser/ui/table_view/feature_flags.mm
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/table_view/feature_flags.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+const base::Feature kCollectionsCardPresentationStyle{
+    "CollectionsCardPresentationStyle", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsCollectionsCardPresentationStyleEnabled() {
+  return base::FeatureList::IsEnabled(kCollectionsCardPresentationStyle);
+}
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 8b080be..29f067673c 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -34,7 +34,7 @@
 #include "ios/public/provider/chrome/browser/voice/audio_session_controller.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_provider.h"
 #include "ios/web/public/navigation/browser_url_rewriter.h"
-#include "ios/web/public/service_names.mojom.h"
+#include "ios/web/public/service/service_names.mojom.h"
 #include "ios/web/public/user_agent.h"
 #include "net/http/http_util.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/ios/chrome/browser/web/services_unittest.mm b/ios/chrome/browser/web/services_unittest.mm
index c6f6277..6f44384 100644
--- a/ios/chrome/browser/web/services_unittest.mm
+++ b/ios/chrome/browser/web/services_unittest.mm
@@ -9,7 +9,7 @@
 #include "components/services/unzip/public/mojom/constants.mojom.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
-#include "ios/web/public/service_manager_connection.h"
+#include "ios/web/public/service/service_manager_connection.h"
 #include "ios/web/public/test/scoped_testing_web_client.h"
 #include "ios/web/public/test/test_service_manager_context.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 483459e2..bf6cd01f 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//ios/build/config.gni")
 import("//ios/web/js_compile.gni")
-import("//mojo/public/tools/bindings/mojom.gni")
 import("//testing/test.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -18,16 +17,16 @@
 
 source_set("web") {
   public_deps = [
-    ":service_names",
+    "//ios/web/public/service:service_names",
 
     # TODO(crbug.com/616244): Remove private files from public dependencies.
-    ":services",
     ":threads",
     "//ios/web/navigation:core",
     "//ios/web/net",
     "//ios/web/public",
     "//ios/web/public/download",
     "//ios/web/public/init",
+    "//ios/web/service",
     "//ios/web/web_state:web_state_impl_header",
     "//ios/web/web_state/ui",
     "//ios/web/web_state/ui:wk_web_view_configuration_provider",
@@ -40,7 +39,6 @@
     ":js_resources",
     ":navigation_resources",
     ":resources",
-    ":services",
     ":threads",
     "//base",
     "//ios/web/common",
@@ -53,15 +51,13 @@
     "//ios/web/public/security",
     "//ios/web/public/session",
     "//ios/web/security",
+    "//ios/web/service",
     "//ios/web/session",
     "//ios/web/thread",
     "//ios/web/web_state",
     "//ios/web/web_state:web_view_internal_creation_util",
     "//services/network:network_service",
-    "//services/network/public/mojom",
-    "//services/service_manager",
     "//services/service_manager/public/cpp",
-    "//services/service_manager/public/mojom",
   ]
 
   sources = [
@@ -94,34 +90,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-source_set("services") {
-  deps = [
-    ":service_names",
-    "//ios/web/public",
-    "//services/network:network_service",
-    "//services/network/public/mojom",
-    "//services/service_manager",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/public/mojom",
-  ]
-  sources = [
-    "service_manager_connection_impl.cc",
-    "service_manager_connection_impl.h",
-    "service_manager_context.h",
-    "service_manager_context.mm",
-    "web_browser_manifest.h",
-    "web_browser_manifest.mm",
-  ]
-
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
-mojom("service_names") {
-  sources = [
-    "public/service_names.mojom",
-  ]
-}
-
 source_set("core") {
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
@@ -269,7 +237,6 @@
     "//ios/web/test:test_constants",
     "//ios/web/test:test_support",
     "//net:test_support",
-    "//services/service_manager/public/cpp",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/ocmock",
@@ -280,7 +247,6 @@
     "browser_state_unittest.cc",
     "history_state_util_unittest.mm",
     "network_context_owner_unittest.cc",
-    "service_manager_connection_impl_unittest.cc",
     "test/web_test_unittest.mm",
     "web_client_unittest.mm",
     "web_thread_unittest.cc",
@@ -314,7 +280,6 @@
     "//ios/web/test/fakes",
     "//ios/web/web_state/ui:crw_web_view_navigation_proxy",
     "//net:test_support",
-    "//services/service_manager/public/cpp",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/ocmock",
diff --git a/ios/web/OWNERS b/ios/web/OWNERS
index eb0f3a0a..a7206ed 100644
--- a/ios/web/OWNERS
+++ b/ios/web/OWNERS
@@ -4,8 +4,3 @@
 
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
-
-per-file web_browser_manifest.cc=set noparent
-per-file web_browser_manifest.cc=file://ipc/SECURITY_OWNERS
-per-file web_browser_manifest.h=set noparent
-per-file web_browser_manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/ios/web/browser_state.mm b/ios/web/browser_state.mm
index d0adaf86..44ed2fd 100644
--- a/ios/web/browser_state.mm
+++ b/ios/web/browser_state.mm
@@ -17,8 +17,8 @@
 #include "base/token.h"
 #include "ios/web/public/init/network_context_owner.h"
 #include "ios/web/public/security/certificate_policy_cache.h"
-#include "ios/web/public/service_manager_connection.h"
-#include "ios/web/public/service_names.mojom.h"
+#include "ios/web/public/service/service_manager_connection.h"
+#include "ios/web/public/service/service_names.mojom.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
 #include "ios/web/public/web_client.h"
diff --git a/ios/web/init/BUILD.gn b/ios/web/init/BUILD.gn
index ec5bee26..93c22555 100644
--- a/ios/web/init/BUILD.gn
+++ b/ios/web/init/BUILD.gn
@@ -17,11 +17,11 @@
     "//base",
     "//base:i18n",
     "//crypto",
-    "//ios/web:services",
     "//ios/web:threads",
     "//ios/web/net",
     "//ios/web/public",
     "//ios/web/public/init",
+    "//ios/web/service",
     "//ios/web/webui",
     "//mojo/core/embedder",
     "//net",
diff --git a/ios/web/init/web_main_loop.mm b/ios/web/init/web_main_loop.mm
index 2585687..bb7f858 100644
--- a/ios/web/init/web_main_loop.mm
+++ b/ios/web/init/web_main_loop.mm
@@ -25,7 +25,7 @@
 #include "ios/web/public/init/web_main_parts.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #import "ios/web/public/web_client.h"
-#include "ios/web/service_manager_context.h"
+#include "ios/web/service/service_manager_context.h"
 #include "ios/web/web_sub_thread.h"
 #include "ios/web/web_thread_impl.h"
 #include "ios/web/webui/url_data_manager_ios.h"
diff --git a/ios/web/public/BUILD.gn b/ios/web/public/BUILD.gn
index 1ebc6ea..6cd253eb4 100644
--- a/ios/web/public/BUILD.gn
+++ b/ios/web/public/BUILD.gn
@@ -11,6 +11,7 @@
     ":web_state_types",
     "//ios/web/public/favicon",
     "//ios/web/public/navigation",
+    "//ios/web/public/service",
     "//ios/web/public/thread",
     "//net",
     "//services/network/public/cpp",
@@ -29,7 +30,6 @@
     "java_script_dialog_callback.h",
     "java_script_dialog_presenter.h",
     "java_script_dialog_type.h",
-    "service_manager_connection.h",
     "url_schemes.mm",
     "web_client.h",
     "web_state/page_display_state.mm",
@@ -38,8 +38,6 @@
     "web_state/web_state.h",
     "web_state/web_state_delegate.h",
     "web_state/web_state_delegate_bridge.h",
-    "web_state/web_state_interface_provider.cc",
-    "web_state/web_state_interface_provider.h",
     "web_state/web_state_observer_bridge.h",
     "web_state/web_state_user_data.h",
   ]
diff --git a/ios/web/public/service/BUILD.gn b/ios/web/public/service/BUILD.gn
new file mode 100644
index 0000000..d92ed99
--- /dev/null
+++ b/ios/web/public/service/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+source_set("service") {
+  deps = [
+    "//base",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/public/cpp:cpp_types",
+    "//services/service_manager/public/mojom",
+  ]
+
+  sources = [
+    "service_manager_connection.h",
+    "web_state_interface_provider.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+mojom("service_names") {
+  sources = [
+    "service_names.mojom",
+  ]
+}
diff --git a/ios/web/public/service/OWNERS b/ios/web/public/service/OWNERS
new file mode 100644
index 0000000..4415d23
--- /dev/null
+++ b/ios/web/public/service/OWNERS
@@ -0,0 +1,7 @@
+rockot@google.com
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ios/web/public/service/README.md b/ios/web/public/service/README.md
new file mode 100644
index 0000000..9f8b6a9d
--- /dev/null
+++ b/ios/web/public/service/README.md
@@ -0,0 +1 @@
+This directory contains API for establishing mojo connection with web service.
diff --git a/ios/web/public/service_manager_connection.h b/ios/web/public/service/service_manager_connection.h
similarity index 93%
rename from ios/web/public/service_manager_connection.h
rename to ios/web/public/service/service_manager_connection.h
index b6f2a385..77dffdc 100644
--- a/ios/web/public/service_manager_connection.h
+++ b/ios/web/public/service/service_manager_connection.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_SERVICE_MANAGER_CONNECTION_H_
-#define IOS_WEB_PUBLIC_SERVICE_MANAGER_CONNECTION_H_
+#ifndef IOS_WEB_PUBLIC_SERVICE_SERVICE_MANAGER_CONNECTION_H_
+#define IOS_WEB_PUBLIC_SERVICE_SERVICE_MANAGER_CONNECTION_H_
 
 #include <memory>
 
@@ -69,4 +69,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_PUBLIC_SERVICE_MANAGER_CONNECTION_H_
+#endif  // IOS_WEB_PUBLIC_SERVICE_SERVICE_MANAGER_CONNECTION_H_
diff --git a/ios/web/public/service_names.mojom b/ios/web/public/service/service_names.mojom
similarity index 100%
rename from ios/web/public/service_names.mojom
rename to ios/web/public/service/service_names.mojom
diff --git a/ios/web/public/web_state/web_state_interface_provider.h b/ios/web/public/service/web_state_interface_provider.h
similarity index 84%
rename from ios/web/public/web_state/web_state_interface_provider.h
rename to ios/web/public/service/web_state_interface_provider.h
index 2a31eaca..52ad2681 100644
--- a/ios/web/public/web_state/web_state_interface_provider.h
+++ b/ios/web/public/service/web_state_interface_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_STATE_WEB_STATE_INTERFACE_PROVIDER_H_
-#define IOS_WEB_WEB_STATE_WEB_STATE_INTERFACE_PROVIDER_H_
+#ifndef IOS_WEB_PUBLIC_SERVICE_WEB_STATE_INTERFACE_PROVIDER_H_
+#define IOS_WEB_PUBLIC_SERVICE_WEB_STATE_INTERFACE_PROVIDER_H_
 
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -36,4 +36,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_STATE_WEB_STATE_INTERFACE_PROVIDER_H_
+#endif  // IOS_WEB_PUBLIC_SERVICE_WEB_STATE_INTERFACE_PROVIDER_H_
diff --git a/ios/web/public/test/test_service_manager_context.mm b/ios/web/public/test/test_service_manager_context.mm
index 2ea5d13..6385a789 100644
--- a/ios/web/public/test/test_service_manager_context.mm
+++ b/ios/web/public/test/test_service_manager_context.mm
@@ -4,7 +4,7 @@
 
 #include "ios/web/public/test/test_service_manager_context.h"
 
-#include "ios/web/service_manager_context.h"
+#include "ios/web/service/service_manager_context.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web/service/BUILD.gn b/ios/web/service/BUILD.gn
new file mode 100644
index 0000000..05b529b9
--- /dev/null
+++ b/ios/web/service/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("service") {
+  deps = [
+    "//ios/web/public",
+    "//ios/web/public/service:service_names",
+    "//services/network:network_service",
+    "//services/network/public/mojom",
+    "//services/service_manager",
+    "//services/service_manager/public/cpp",
+    "//services/service_manager/public/mojom",
+  ]
+  sources = [
+    "service_manager_connection_impl.cc",
+    "service_manager_connection_impl.h",
+    "service_manager_context.h",
+    "service_manager_context.mm",
+    "web_browser_manifest.h",
+    "web_browser_manifest.mm",
+    "web_state_interface_provider.cc",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unittests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  deps = [
+    ":service",
+    "//base",
+    "//base/test:test_support",
+    "//ios/web/public/test",
+    "//services/service_manager/public/cpp",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "service_manager_connection_impl_unittest.cc",
+  ]
+}
diff --git a/ios/web/service/OWNERS b/ios/web/service/OWNERS
new file mode 100644
index 0000000..b225ddf
--- /dev/null
+++ b/ios/web/service/OWNERS
@@ -0,0 +1,9 @@
+rockot@google.com
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
+
+per-file web_browser_manifest.cc=set noparent
+per-file web_browser_manifest.cc=file://ipc/SECURITY_OWNERS
+per-file web_browser_manifest.h=set noparent
+per-file web_browser_manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/ios/web/service_manager_connection_impl.cc b/ios/web/service/service_manager_connection_impl.cc
similarity index 96%
rename from ios/web/service_manager_connection_impl.cc
rename to ios/web/service/service_manager_connection_impl.cc
index 52883c7..0ad2047 100644
--- a/ios/web/service_manager_connection_impl.cc
+++ b/ios/web/service/service_manager_connection_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/service_manager_connection_impl.h"
+#include "ios/web/service/service_manager_connection_impl.h"
 
 #include <queue>
 #include <utility>
@@ -39,10 +39,9 @@
     : public base::RefCountedThreadSafe<IOThreadContext>,
       public service_manager::Service {
  public:
-  IOThreadContext(
-      service_manager::mojom::ServiceRequest service_request,
-      scoped_refptr<base::SequencedTaskRunner> io_task_runner,
-      service_manager::mojom::ConnectorRequest connector_request)
+  IOThreadContext(service_manager::mojom::ServiceRequest service_request,
+                  scoped_refptr<base::SequencedTaskRunner> io_task_runner,
+                  service_manager::mojom::ConnectorRequest connector_request)
       : pending_service_request_(std::move(service_request)),
         io_task_runner_(io_task_runner),
         pending_connector_request_(std::move(connector_request)),
diff --git a/ios/web/service_manager_connection_impl.h b/ios/web/service/service_manager_connection_impl.h
similarity index 87%
rename from ios/web/service_manager_connection_impl.h
rename to ios/web/service/service_manager_connection_impl.h
index db13b40..b5e19deba 100644
--- a/ios/web/service_manager_connection_impl.h
+++ b/ios/web/service/service_manager_connection_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_SERVICE_MANAGER_SERVICE_MANAGER_CONNECTION_IMPL_H_
-#define IOS_WEB_SERVICE_MANAGER_SERVICE_MANAGER_CONNECTION_IMPL_H_
+#ifndef IOS_WEB_SERVICE_SERVICE_MANAGER_CONNECTION_IMPL_H_
+#define IOS_WEB_SERVICE_SERVICE_MANAGER_CONNECTION_IMPL_H_
 
 #include <memory>
 
@@ -11,7 +11,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
-#include "ios/web/public/service_manager_connection.h"
+#include "ios/web/public/service/service_manager_connection.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/service_manager/public/cpp/identity.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
@@ -63,4 +63,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_SERVICE_MANAGER_SERVICE_MANAGER_CONNECTION_IMPL_H_
+#endif  // IOS_WEB_SERVICE_SERVICE_MANAGER_CONNECTION_IMPL_H_
diff --git a/ios/web/service_manager_connection_impl_unittest.cc b/ios/web/service/service_manager_connection_impl_unittest.cc
similarity index 96%
rename from ios/web/service_manager_connection_impl_unittest.cc
rename to ios/web/service/service_manager_connection_impl_unittest.cc
index c1807cb5..7b10562 100644
--- a/ios/web/service_manager_connection_impl_unittest.cc
+++ b/ios/web/service/service_manager_connection_impl_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/service_manager_connection_impl.h"
+#include "ios/web/service/service_manager_connection_impl.h"
 
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
diff --git a/ios/web/service_manager_context.h b/ios/web/service/service_manager_context.h
similarity index 89%
rename from ios/web/service_manager_context.h
rename to ios/web/service/service_manager_context.h
index 3fc87e7..bd8ba3b 100644
--- a/ios/web/service_manager_context.h
+++ b/ios/web/service/service_manager_context.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_SERVICE_MANAGER_CONTEXT_H_
-#define IOS_WEB_SERVICE_MANAGER_CONTEXT_H_
+#ifndef IOS_WEB_SERVICE_SERVICE_MANAGER_CONTEXT_H_
+#define IOS_WEB_SERVICE_SERVICE_MANAGER_CONTEXT_H_
 
 #include <string>
 
@@ -42,4 +42,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_SERVICE_MANAGER_CONTEXT_H_
+#endif  // IOS_WEB_SERVICE_SERVICE_MANAGER_CONTEXT_H_
diff --git a/ios/web/service_manager_context.mm b/ios/web/service/service_manager_context.mm
similarity index 96%
rename from ios/web/service_manager_context.mm
rename to ios/web/service/service_manager_context.mm
index 6b69f44..017aab8 100644
--- a/ios/web/service_manager_context.mm
+++ b/ios/web/service/service_manager_context.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/service_manager_context.h"
+#include "ios/web/service/service_manager_context.h"
 
 #include <algorithm>
 #include <memory>
@@ -17,13 +17,13 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
-#include "ios/web/public/service_manager_connection.h"
+#include "ios/web/public/service/service_manager_connection.h"
 #include "ios/web/public/service_names.mojom.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
 #include "ios/web/public/web_client.h"
-#include "ios/web/service_manager_connection_impl.h"
-#import "ios/web/web_browser_manifest.h"
+#include "ios/web/service/service_manager_connection_impl.h"
+#import "ios/web/service/web_browser_manifest.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/constants.h"
@@ -155,9 +155,7 @@
     metadata->SetPID(base::GetCurrentProcId());
   }
 
-  void ShutDownOnIOThread() {
-    service_manager_.reset();
-  }
+  void ShutDownOnIOThread() { service_manager_.reset(); }
 
   std::unique_ptr<service_manager::ServiceManager> service_manager_;
 
diff --git a/ios/web/web_browser_manifest.h b/ios/web/service/web_browser_manifest.h
similarity index 80%
rename from ios/web/web_browser_manifest.h
rename to ios/web/service/web_browser_manifest.h
index 5b7184cf..0d5ea811c 100644
--- a/ios/web/web_browser_manifest.h
+++ b/ios/web/service/web_browser_manifest.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_WEB_BROWSER_MANIFEST_H_
-#define IOS_WEB_WEB_BROWSER_MANIFEST_H_
+#ifndef IOS_WEB_SERVICE_WEB_BROWSER_MANIFEST_H_
+#define IOS_WEB_SERVICE_WEB_BROWSER_MANIFEST_H_
 
 #include "services/service_manager/public/cpp/manifest.h"
 
@@ -17,4 +17,4 @@
 
 }  // namespace web
 
-#endif  // IOS_WEB_WEB_BROWSER_MANIFEST_H_
+#endif  // IOS_WEB_SERVICE_WEB_BROWSER_MANIFEST_H_
diff --git a/ios/web/web_browser_manifest.mm b/ios/web/service/web_browser_manifest.mm
similarity index 96%
rename from ios/web/web_browser_manifest.mm
rename to ios/web/service/web_browser_manifest.mm
index 20a42b8..c63ef915b 100644
--- a/ios/web/web_browser_manifest.mm
+++ b/ios/web/service/web_browser_manifest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_browser_manifest.h"
+#import "ios/web/service/web_browser_manifest.h"
 
 #include "base/no_destructor.h"
 #include "ios/web/public/service_names.mojom.h"
diff --git a/ios/web/public/web_state/web_state_interface_provider.cc b/ios/web/service/web_state_interface_provider.cc
similarity index 90%
rename from ios/web/public/web_state/web_state_interface_provider.cc
rename to ios/web/service/web_state_interface_provider.cc
index fe308cd..fe921770 100644
--- a/ios/web/public/web_state/web_state_interface_provider.cc
+++ b/ios/web/service/web_state_interface_provider.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/web/public/web_state/web_state_interface_provider.h"
+#include "ios/web/public/service/web_state_interface_provider.h"
 
 namespace web {
 
diff --git a/ios/web/shell/shell_web_client.mm b/ios/web/shell/shell_web_client.mm
index 62ac42de..6f54cae 100644
--- a/ios/web/shell/shell_web_client.mm
+++ b/ios/web/shell/shell_web_client.mm
@@ -7,7 +7,7 @@
 #import <UIKit/UIKit.h>
 
 #include "base/bind.h"
-#include "ios/web/public/service_names.mojom.h"
+#include "ios/web/public/service/service_names.mojom.h"
 #include "ios/web/public/user_agent.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ios/web/shell/shell_web_main_parts.h"
diff --git a/ios/web/shell/test/earl_grey/service_manager_app_interface.mm b/ios/web/shell/test/earl_grey/service_manager_app_interface.mm
index eb746e75..c11b300 100644
--- a/ios/web/shell/test/earl_grey/service_manager_app_interface.mm
+++ b/ios/web/shell/test/earl_grey/service_manager_app_interface.mm
@@ -9,7 +9,7 @@
 #import "base/strings/sys_string_conversions.h"
 #import "ios/testing/earl_grey/earl_grey_app.h"
 #include "ios/web/public/browser_state.h"
-#include "ios/web/public/service_manager_connection.h"
+#include "ios/web/public/service/service_manager_connection.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/shell/test/app/web_shell_test_util.h"
 #import "ios/web/shell/web_usage_controller.mojom.h"
diff --git a/ios/web/web_state/ui/crw_wk_ui_handler.mm b/ios/web/web_state/ui/crw_wk_ui_handler.mm
index 0b30802..f17a364 100644
--- a/ios/web/web_state/ui/crw_wk_ui_handler.mm
+++ b/ios/web/web_state/ui/crw_wk_ui_handler.mm
@@ -8,8 +8,8 @@
 #import "ios/web/navigation/wk_navigation_action_util.h"
 #import "ios/web/navigation/wk_navigation_util.h"
 #import "ios/web/public/java_script_dialog_type.h"
+#include "ios/web/public/service/web_state_interface_provider.h"
 #import "ios/web/public/web_client.h"
-#include "ios/web/public/web_state/web_state_interface_provider.h"
 #import "ios/web/web_state/ui/crw_context_menu_controller.h"
 #import "ios/web/web_state/ui/crw_wk_ui_handler_delegate.h"
 #import "ios/web/web_state/user_interaction_state.h"
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index cb7dd161..e7ecb97 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -30,6 +30,7 @@
 #import "ios/web/public/java_script_dialog_presenter.h"
 #import "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/web_state_policy_decider.h"
+#include "ios/web/public/service/web_state_interface_provider.h"
 #import "ios/web/public/session/crw_navigation_item_storage.h"
 #import "ios/web/public/session/crw_session_storage.h"
 #import "ios/web/public/session/serializable_user_data_manager.h"
@@ -37,7 +38,6 @@
 #import "ios/web/public/web_client.h"
 #import "ios/web/public/web_state/context_menu_params.h"
 #import "ios/web/public/web_state/web_state_delegate.h"
-#include "ios/web/public/web_state/web_state_interface_provider.h"
 #include "ios/web/public/web_state/web_state_observer.h"
 #include "ios/web/public/webui/web_ui_ios_controller.h"
 #import "ios/web/security/web_interstitial_impl.h"
diff --git a/ios/web/webui/mojo_facade_unittest.mm b/ios/web/webui/mojo_facade_unittest.mm
index 55fcb4d..f7a02719 100644
--- a/ios/web/webui/mojo_facade_unittest.mm
+++ b/ios/web/webui/mojo_facade_unittest.mm
@@ -11,9 +11,9 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#include "ios/web/public/service/web_state_interface_provider.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/web_test.h"
-#include "ios/web/public/web_state/web_state_interface_provider.h"
 #include "ios/web/test/mojo_test.mojom.h"
 #include "ios/web/web_state/web_state_impl.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
diff --git a/ios/web/webui/web_ui_mojo_inttest.mm b/ios/web/webui/web_ui_mojo_inttest.mm
index e64ae51..c09d77b3 100644
--- a/ios/web/webui/web_ui_mojo_inttest.mm
+++ b/ios/web/webui/web_ui_mojo_inttest.mm
@@ -10,9 +10,9 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "ios/web/grit/ios_web_resources.h"
 #import "ios/web/public/navigation/navigation_manager.h"
+#include "ios/web/public/service/web_state_interface_provider.h"
 #import "ios/web/public/test/navigation_test_util.h"
 #import "ios/web/public/web_state/web_state.h"
-#include "ios/web/public/web_state/web_state_interface_provider.h"
 #include "ios/web/public/webui/web_ui_ios_controller.h"
 #include "ios/web/public/webui/web_ui_ios_controller_factory.h"
 #include "ios/web/public/webui/web_ui_ios_data_source.h"
diff --git a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
index 5b5f7b41..94f359a 100644
--- a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
+++ b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
@@ -11,6 +11,7 @@
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/signin/public/base/device_id_helper.h"
 #include "components/sync/model/model_type_store_service.h"
+#include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_device_info/device_info_sync_service_impl.h"
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 #include "components/version_info/version_info.h"
@@ -60,10 +61,13 @@
           /*send_tab_to_self_receiving_enabled_callback=*/
           base::BindRepeating([]() { return false; }));
 
+  auto device_prefs =
+      std::make_unique<syncer::DeviceInfoPrefs>(browser_state->GetPrefs());
+
   return std::make_unique<syncer::DeviceInfoSyncServiceImpl>(
       WebViewModelTypeStoreServiceFactory::GetForBrowserState(browser_state)
           ->GetStoreFactory(),
-      std::move(local_device_info_provider));
+      std::move(local_device_info_provider), std::move(device_prefs));
 }
 
 }  // namespace ios_web_view
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 27efa60e..ba4aa20 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -584,6 +584,7 @@
       "windows/d3d11_texture_selector_unittest.cc",
       "windows/d3d11_video_decoder_unittest.cc",
       "windows/d3d11_video_processor_proxy_unittest.cc",
+      "windows/supported_profile_helpers_unittest.cc",
     ]
     libs = [ "dxguid.lib" ]
   }
diff --git a/media/gpu/vaapi/BUILD.gn b/media/gpu/vaapi/BUILD.gn
index 8481f9d..842a080 100644
--- a/media/gpu/vaapi/BUILD.gn
+++ b/media/gpu/vaapi/BUILD.gn
@@ -65,6 +65,8 @@
     "vaapi_vp8_accelerator.h",
     "vaapi_vp9_accelerator.cc",
     "vaapi_vp9_accelerator.h",
+    "vaapi_webp_decoder.cc",
+    "vaapi_webp_decoder.h",
     "vaapi_wrapper.cc",
     "vaapi_wrapper.h",
     "vp8_encoder.cc",
@@ -139,6 +141,24 @@
   }
 }
 
+source_set("vaapi_test_utils") {
+  testonly = true
+  sources = [
+    "test_utils.cc",
+    "test_utils.h",
+  ]
+  deps = [
+    ":vaapi",
+    "//base",
+    "//media",
+    "//testing/gtest",
+    "//third_party/libyuv:libyuv",
+  ]
+  public_deps = [
+    "//ui/gfx/geometry",
+  ]
+}
+
 source_set("unit_test") {
   testonly = true
   sources = [
@@ -164,6 +184,7 @@
   ]
   deps = [
     ":vaapi",
+    ":vaapi_test_utils",
     ":vaapi_utils_unittest",
     "//base",
     "//media:test_support",
@@ -179,6 +200,27 @@
   }
 }
 
+source_set("webp_decoder_unit_test") {
+  testonly = true
+  sources = [
+    "vaapi_webp_decoder_unittest.cc",
+  ]
+  deps = [
+    ":vaapi",
+    ":vaapi_test_utils",
+    "//base",
+    "//media:test_support",
+    "//media/parsers",
+    "//testing/gtest",
+    "//third_party/libwebp:libwebp_webp",
+    "//ui/gfx:memory_buffer",
+    "//ui/gfx/geometry",
+  ]
+  if (is_chromeos) {
+    deps += [ "//media/capture:chromeos_test_utils" ]
+  }
+}
+
 source_set("vaapi_utils_unittest") {
   testonly = true
   sources = [
diff --git a/media/gpu/vaapi/DEPS b/media/gpu/vaapi/DEPS
new file mode 100644
index 0000000..3fad129
--- /dev/null
+++ b/media/gpu/vaapi/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  ".*_unittest\.cc": [
+    "+third_party/libwebp",
+  ],
+}
diff --git a/media/gpu/vaapi/test_utils.cc b/media/gpu/vaapi/test_utils.cc
new file mode 100644
index 0000000..2bc0f77
--- /dev/null
+++ b/media/gpu/vaapi/test_utils.cc
@@ -0,0 +1,133 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/vaapi/test_utils.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "media/base/video_types.h"
+#include "media/gpu/vaapi/vaapi_utils.h"
+#include "third_party/libyuv/include/libyuv.h"
+
+namespace media {
+namespace vaapi_test_utils {
+
+std::string TestParamToString(
+    const testing::TestParamInfo<TestParam>& param_info) {
+  return param_info.param.test_name;
+}
+
+DecodedImage ScopedVAImageToDecodedImage(const ScopedVAImage* scoped_va_image) {
+  DecodedImage decoded_image{};
+
+  decoded_image.fourcc = scoped_va_image->image()->format.fourcc;
+  decoded_image.number_of_planes = scoped_va_image->image()->num_planes;
+  decoded_image.size =
+      gfx::Size(base::strict_cast<int>(scoped_va_image->image()->width),
+                base::strict_cast<int>(scoped_va_image->image()->height));
+
+  DCHECK_LE(base::strict_cast<size_t>(decoded_image.number_of_planes),
+            kMaxNumberPlanes);
+
+  // This is safe because |number_of_planes| is retrieved from the VA-API and it
+  // can not be greater than 3, which is also the size of the |planes| array.
+  for (uint32_t i = 0u; i < decoded_image.number_of_planes; ++i) {
+    decoded_image.planes[i].data =
+        static_cast<uint8_t*>(scoped_va_image->va_buffer()->data()) +
+        scoped_va_image->image()->offsets[i];
+    decoded_image.planes[i].stride =
+        base::checked_cast<int>(scoped_va_image->image()->pitches[i]);
+  }
+
+  return decoded_image;
+}
+
+bool CompareImages(const DecodedImage& reference_image,
+                   const DecodedImage& hw_decoded_image,
+                   double min_ssim) {
+  if (reference_image.fourcc != VA_FOURCC_I420)
+    return false;
+
+  // Uses the reference image's size as the ground truth.
+  // Note the use of > instead of !=. This is to handle the case in the Intel
+  // iHD driver where, for example, the size of an image is 1280x720 while the
+  // size of the VAAPI surface is 1280x736 because of additional alignment. See
+  // https://git.io/fj6nA.
+  const gfx::Size image_size = reference_image.size;
+  if (image_size.width() > hw_decoded_image.size.width() ||
+      image_size.height() > hw_decoded_image.size.height()) {
+    DLOG(ERROR) << "Wrong expected software decoded image size, "
+                << image_size.ToString() << " versus VaAPI provided "
+                << hw_decoded_image.size.ToString();
+    return false;
+  }
+
+  double ssim = 0;
+  const uint32_t hw_fourcc = hw_decoded_image.fourcc;
+  if (hw_fourcc == VA_FOURCC_I420) {
+    ssim = libyuv::I420Ssim(
+        reference_image.planes[0].data, reference_image.planes[0].stride,
+        reference_image.planes[1].data, reference_image.planes[1].stride,
+        reference_image.planes[2].data, reference_image.planes[2].stride,
+        hw_decoded_image.planes[0].data, hw_decoded_image.planes[0].stride,
+        hw_decoded_image.planes[1].data, hw_decoded_image.planes[1].stride,
+        hw_decoded_image.planes[2].data, hw_decoded_image.planes[2].stride,
+        image_size.width(), image_size.height());
+  } else if (hw_fourcc == VA_FOURCC_NV12 || hw_fourcc == VA_FOURCC_YUY2 ||
+             hw_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
+    // Calculate the stride for the chroma planes.
+    const gfx::Size half_image_size((image_size.width() + 1) / 2,
+                                    (image_size.height() + 1) / 2);
+    // Temporary planes to hold intermediate conversions to I420 (i.e. NV12 to
+    // I420 or YUYV/2 to I420).
+    auto temp_y = std::make_unique<uint8_t[]>(image_size.GetArea());
+    auto temp_u = std::make_unique<uint8_t[]>(half_image_size.GetArea());
+    auto temp_v = std::make_unique<uint8_t[]>(half_image_size.GetArea());
+    int conversion_result = -1;
+
+    if (hw_fourcc == VA_FOURCC_NV12) {
+      conversion_result = libyuv::NV12ToI420(
+          hw_decoded_image.planes[0].data, hw_decoded_image.planes[0].stride,
+          hw_decoded_image.planes[1].data, hw_decoded_image.planes[1].stride,
+          temp_y.get(), image_size.width(), temp_u.get(),
+          half_image_size.width(), temp_v.get(), half_image_size.width(),
+          image_size.width(), image_size.height());
+    } else {
+      // |hw_fourcc| is YUY2 or YUYV, which are handled the same.
+      // TODO(crbug.com/868400): support other formats/planarities/pitches.
+      conversion_result = libyuv::YUY2ToI420(
+          hw_decoded_image.planes[0].data, hw_decoded_image.planes[0].stride,
+          temp_y.get(), image_size.width(), temp_u.get(),
+          half_image_size.width(), temp_v.get(), half_image_size.width(),
+          image_size.width(), image_size.height());
+    }
+    if (conversion_result != 0) {
+      DLOG(ERROR) << "libyuv conversion error";
+      return false;
+    }
+
+    ssim = libyuv::I420Ssim(
+        reference_image.planes[0].data, reference_image.planes[0].stride,
+        reference_image.planes[1].data, reference_image.planes[1].stride,
+        reference_image.planes[2].data, reference_image.planes[2].stride,
+        temp_y.get(), image_size.width(), temp_u.get(), half_image_size.width(),
+        temp_v.get(), half_image_size.width(), image_size.width(),
+        image_size.height());
+  } else {
+    DLOG(ERROR) << "HW FourCC not supported: " << FourccToString(hw_fourcc);
+    return false;
+  }
+
+  if (ssim < min_ssim) {
+    DLOG(ERROR) << "SSIM too low: " << ssim << " < " << min_ssim;
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace vaapi_test_utils
+}  // namespace media
diff --git a/media/gpu/vaapi/test_utils.h b/media/gpu/vaapi/test_utils.h
new file mode 100644
index 0000000..bee8e5d
--- /dev/null
+++ b/media/gpu/vaapi/test_utils.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_VAAPI_TEST_UTILS_H_
+#define MEDIA_GPU_VAAPI_TEST_UTILS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <va/va.h>
+
+#include <string>
+
+// This has to be included first.
+// See http://code.google.com/p/googletest/issues/detail?id=371
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/stl_util.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+class ScopedVAImage;
+
+namespace vaapi_test_utils {
+
+struct TestParam {
+  const char* test_name;
+  const char* filename;
+};
+
+std::string TestParamToString(
+    const testing::TestParamInfo<TestParam>& param_info);
+
+constexpr size_t kMaxNumberPlanes = base::size(VAImage().pitches);
+static_assert(kMaxNumberPlanes <= 3u, "The number of planes should be <= 3");
+static_assert(
+    base::size(VAImage().pitches) == base::size(VAImage().offsets),
+    "The number of VAImage pitches is not equal to the number of offsets");
+
+// A structure to hold generic image decodes in planar format.
+struct DecodedImage {
+  uint32_t fourcc;
+  uint32_t number_of_planes;  // Can not be greater than kMaxNumberPlanes.
+  gfx::Size size;
+  struct {
+    uint8_t* data;
+    int stride;
+  } planes[kMaxNumberPlanes];
+};
+
+// Takes a ScopedVAImage and returns a DecodedImage object that represents
+// the same decoded result.
+DecodedImage ScopedVAImageToDecodedImage(const ScopedVAImage* scoped_va_image);
+
+// Compares the result of sw decoding |reference_image| with |hw_decoded_image|
+// using SSIM. Returns true if all conversions work and SSIM is at least
+// |min_ssim|, or false otherwise. Note that |reference_image| must be given in
+// I420 format.
+bool CompareImages(const DecodedImage& reference_image,
+                   const DecodedImage& hw_decoded_image,
+                   double min_ssim = 0.995);
+
+}  // namespace vaapi_test_utils
+}  // namespace media
+
+#endif  // MEDIA_GPU_VAAPI_TEST_UTILS_H_
diff --git a/media/gpu/vaapi/vaapi_image_decoder.h b/media/gpu/vaapi/vaapi_image_decoder.h
index 09b0d396..68d64ee 100644
--- a/media/gpu/vaapi/vaapi_image_decoder.h
+++ b/media/gpu/vaapi/vaapi_image_decoder.h
@@ -89,6 +89,11 @@
  protected:
   explicit VaapiImageDecoder(VAProfile va_profile);
 
+  ScopedVAContextAndSurface scoped_va_context_and_surface_;
+
+  scoped_refptr<VaapiWrapper> vaapi_wrapper_;
+
+ private:
   // Submits an image to the VA-API by filling its parameters and calling on the
   // corresponding methods according to the image in |encoded_image|. Returns a
   // VaapiImageDecodeStatus that will indicate whether the operation succeeded
@@ -96,11 +101,6 @@
   virtual VaapiImageDecodeStatus AllocateVASurfaceAndSubmitVABuffers(
       base::span<const uint8_t> encoded_image) = 0;
 
-  ScopedVAContextAndSurface scoped_va_context_and_surface_;
-
-  scoped_refptr<VaapiWrapper> vaapi_wrapper_;
-
- private:
   // The VA profile used for the current image decoder.
   const VAProfile va_profile_;
 
diff --git a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.cc b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.cc
index 5ac2239..4ed5851 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.cc
@@ -4,10 +4,10 @@
 
 #include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.h"
 
-#include <utility>
-
 #include <va/va.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/containers/span.h"
 #include "base/location.h"
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder.cc b/media/gpu/vaapi/vaapi_jpeg_decoder.cc
index 988a6d2..8658551 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.cc
@@ -5,12 +5,11 @@
 #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
 
 #include <string.h>
+#include <va/va.h>
 
 #include <iostream>
 #include <type_traits>
 
-#include <va/va.h>
-
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/stl_util.h"
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder.h b/media/gpu/vaapi/vaapi_jpeg_decoder.h
index 2c8ecca7..1c74235 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder.h
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.h
@@ -22,8 +22,6 @@
 // or 4:4:4, returns kInvalidVaRtFormat.
 unsigned int VaSurfaceFormatForJpeg(const JpegFrameHeader& frame_header);
 
-// Initializes a VaapiWrapper for the purpose of performing
-// hardware-accelerated JPEG decodes.
 class VaapiJpegDecoder : public VaapiImageDecoder {
  public:
   VaapiJpegDecoder();
@@ -40,7 +38,7 @@
   std::unique_ptr<ScopedVAImage> GetImage(uint32_t preferred_image_fourcc,
                                           VaapiImageDecodeStatus* status);
 
- protected:
+ private:
   // VaapiImageDecoder implementation.
   VaapiImageDecodeStatus AllocateVASurfaceAndSubmitVABuffers(
       base::span<const uint8_t> encoded_image) override;
diff --git a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
index ad383773..5ec03fef 100644
--- a/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
@@ -4,14 +4,13 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <va/va.h>
 
 #include <algorithm>
 #include <memory>
 #include <string>
 #include <vector>
 
-#include <va/va.h>
-
 // This has to be included first.
 // See http://code.google.com/p/googletest/issues/detail?id=371
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,11 +23,11 @@
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/numerics/safe_conversions.h"
-#include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "media/base/test_data_util.h"
 #include "media/base/video_types.h"
 #include "media/capture/video/chromeos/local_gpu_memory_buffer_manager.h"
+#include "media/gpu/vaapi/test_utils.h"
 #include "media/gpu/vaapi/va_surface.h"
 #include "media/gpu/vaapi/vaapi_image_decoder.h"
 #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
@@ -51,6 +50,8 @@
 namespace media {
 namespace {
 
+using DecodedImagePtr = std::unique_ptr<vaapi_test_utils::DecodedImage>;
+
 constexpr const char* kYuv422Filename = "pixel-1280x720.jpg";
 constexpr const char* kYuv420Filename = "pixel-1280x720-yuv420.jpg";
 constexpr const char* kYuv444Filename = "pixel-1280x720-yuv444.jpg";
@@ -58,12 +59,7 @@
 constexpr const char* kOddWidthImageFilename = "peach_pi-41x22.jpg";
 constexpr const char* kOddDimensionsImageFilename = "peach_pi-41x23.jpg";
 
-struct TestParam {
-  const char* test_name;
-  const char* filename;
-};
-
-const TestParam kTestCases[] = {
+const vaapi_test_utils::TestParam kTestCases[] = {
     {"YUV422", kYuv422Filename},
     {"YUV420", kYuv420Filename},
     {"YUV444", kYuv444Filename},
@@ -72,22 +68,6 @@
     {"OddDimensionsImage41x23", kOddDimensionsImageFilename},
 };
 
-constexpr size_t kMaxNumberPlanes = base::size(VAImage().pitches);
-static_assert(kMaxNumberPlanes <= 3u, "The number of planes should be <= 3");
-static_assert(
-    base::size(VAImage().pitches) == base::size(VAImage().offsets),
-    "The number of VAImage pitches is not equal to the number of offsets");
-
-struct DecodedVAImage {
-  uint32_t va_fourcc;
-  uint32_t number_of_planes;  // Can not be greater than kMaxNumberPlanes.
-  gfx::Size coded_size;
-  struct {
-    uint8_t* data;
-    int stride;
-  } planes[kMaxNumberPlanes];
-};
-
 constexpr double kMinSsim = 0.995;
 
 // This file is not supported by the VAAPI, so we don't define expectations on
@@ -102,131 +82,55 @@
 // JPEG decoding.
 constexpr gfx::Size kLargestSupportedSize(16 * 1024, 16 * 1024);
 
-// Takes a ScopedVAImage and returns a DecodedVAImage object that represents
-// the same decoded result.
-DecodedVAImage ScopedVAImageToDecodedVAImage(
-    const ScopedVAImage* decoded_image) {
-  DecodedVAImage converted_result{};
-
-  converted_result.va_fourcc = decoded_image->image()->format.fourcc;
-  converted_result.number_of_planes = decoded_image->image()->num_planes;
-  converted_result.coded_size =
-      gfx::Size(base::strict_cast<int>(decoded_image->image()->width),
-                base::strict_cast<int>(decoded_image->image()->height));
-
-  DCHECK_LE(base::strict_cast<size_t>(converted_result.number_of_planes),
-            kMaxNumberPlanes);
-
-  // This is safe because |number_of_planes| is retrieved from the VA-API and it
-  // can not be greater than 3, which is also the size of the |planes| array.
-  for (uint32_t i = 0u; i < converted_result.number_of_planes; ++i) {
-    converted_result.planes[i].data =
-        static_cast<uint8_t*>(decoded_image->va_buffer()->data()) +
-        decoded_image->image()->offsets[i];
-    converted_result.planes[i].stride =
-        base::checked_cast<int>(decoded_image->image()->pitches[i]);
-  }
-
-  return converted_result;
-}
-
-// Compares the result of sw decoding |encoded_image| with |decoded_image| using
-// SSIM. Returns true if all conversions work and SSIM is above a given
-// threshold (kMinSsim), or false otherwise.
-bool CompareImages(base::span<const uint8_t> encoded_image,
-                   const DecodedVAImage& decoded_image) {
+// Decodes the given |encoded_image| using libyuv and returns the result as a
+// DecodedImage object. The decoded planes will be stored in |dest_*|.
+// Note however, that this function does not takes ownership of |dest_*| or
+// manage their memory.
+DecodedImagePtr GetSwDecode(base::span<const uint8_t> encoded_image,
+                            std::vector<uint8_t>* dest_y,
+                            std::vector<uint8_t>* dest_u,
+                            std::vector<uint8_t>* dest_v) {
+  DCHECK(dest_y && dest_u && dest_v);
   JpegParseResult parse_result;
   const bool result = ParseJpegPicture(encoded_image.data(),
                                        encoded_image.size(), &parse_result);
   if (!result)
-    return false;
+    return nullptr;
 
-  const int coded_width =
-      base::strict_cast<int>(parse_result.frame_header.coded_width);
-  const int coded_height =
-      base::strict_cast<int>(parse_result.frame_header.coded_height);
-  // Note the use of > instead of !=. This is to handle the case in the Intel
-  // iHD driver where the coded size we compute for one of the images is
-  // 1280x720 while the size of the VAAPI surface is 1280x736 because of
-  // additional alignment. See https://git.io/fj6nA.
-  if (coded_width > decoded_image.coded_size.width() ||
-      coded_height > decoded_image.coded_size.height()) {
-    DLOG(ERROR) << "Wrong expected decoded JPEG coded size, " << coded_width
-                << "x" << coded_height << " versus VaAPI provided "
-                << decoded_image.coded_size.width() << "x"
-                << decoded_image.coded_size.height();
-    return false;
+  const gfx::Size jpeg_size(
+      base::strict_cast<int>(parse_result.frame_header.visible_width),
+      base::strict_cast<int>(parse_result.frame_header.visible_height));
+  if (jpeg_size.IsEmpty())
+    return nullptr;
+
+  const gfx::Size half_jpeg_size((jpeg_size.width() + 1) / 2,
+                                 (jpeg_size.height() + 1) / 2);
+
+  dest_y->resize(jpeg_size.GetArea());
+  dest_u->resize(half_jpeg_size.GetArea());
+  dest_v->resize(half_jpeg_size.GetArea());
+
+  if (libyuv::ConvertToI420(
+          encoded_image.data(), encoded_image.size(), dest_y->data(),
+          jpeg_size.width(), dest_u->data(), half_jpeg_size.width(),
+          dest_v->data(), half_jpeg_size.width(), 0, 0, jpeg_size.width(),
+          jpeg_size.height(), jpeg_size.width(), jpeg_size.height(),
+          libyuv::kRotate0, libyuv::FOURCC_MJPG) != 0) {
+    return nullptr;
   }
 
-  const uint16_t width = parse_result.frame_header.visible_width;
-  const uint16_t height = parse_result.frame_header.visible_height;
-  const uint16_t half_width = (width + 1) / 2;
-  const uint16_t half_height = (height + 1) / 2;
+  auto sw_decoded_jpeg = std::make_unique<vaapi_test_utils::DecodedImage>();
+  sw_decoded_jpeg->fourcc = VA_FOURCC_I420;
+  sw_decoded_jpeg->number_of_planes = 3u;
+  sw_decoded_jpeg->size = jpeg_size;
+  sw_decoded_jpeg->planes[0].data = dest_y->data();
+  sw_decoded_jpeg->planes[0].stride = jpeg_size.width();
+  sw_decoded_jpeg->planes[1].data = dest_u->data();
+  sw_decoded_jpeg->planes[1].stride = half_jpeg_size.width();
+  sw_decoded_jpeg->planes[2].data = dest_v->data();
+  sw_decoded_jpeg->planes[2].stride = half_jpeg_size.width();
 
-  auto libyuv_y_plane = std::make_unique<uint8_t[]>(width * height);
-  auto libyuv_u_plane = std::make_unique<uint8_t[]>(half_width * half_height);
-  auto libyuv_v_plane = std::make_unique<uint8_t[]>(half_width * half_height);
-
-  int conversion_result = libyuv::ConvertToI420(
-      encoded_image.data(), encoded_image.size(), libyuv_y_plane.get(), width,
-      libyuv_u_plane.get(), half_width, libyuv_v_plane.get(), half_width, 0, 0,
-      width, height, width, height, libyuv::kRotate0, libyuv::FOURCC_MJPG);
-  if (conversion_result != 0) {
-    DLOG(ERROR) << "libyuv conversion error";
-    return false;
-  }
-
-  const uint32_t va_fourcc = decoded_image.va_fourcc;
-  double ssim = 0;
-  if (va_fourcc == VA_FOURCC_I420) {
-    ssim = libyuv::I420Ssim(
-        libyuv_y_plane.get(), width, libyuv_u_plane.get(), half_width,
-        libyuv_v_plane.get(), half_width, decoded_image.planes[0].data,
-        decoded_image.planes[0].stride, decoded_image.planes[1].data,
-        decoded_image.planes[1].stride, decoded_image.planes[2].data,
-        decoded_image.planes[2].stride, width, height);
-  } else if (va_fourcc == VA_FOURCC_NV12 || va_fourcc == VA_FOURCC_YUY2 ||
-             va_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
-    // Temporary planes to hold intermediate conversions to I420 (i.e. NV12 to
-    // I420 or YUYV/2 to I420).
-    auto temp_y = std::make_unique<uint8_t[]>(width * height);
-    auto temp_u = std::make_unique<uint8_t[]>(half_width * half_height);
-    auto temp_v = std::make_unique<uint8_t[]>(half_width * half_height);
-
-    if (va_fourcc == VA_FOURCC_NV12) {
-      conversion_result = libyuv::NV12ToI420(
-          decoded_image.planes[0].data, decoded_image.planes[0].stride,
-          decoded_image.planes[1].data, decoded_image.planes[1].stride,
-          temp_y.get(), width, temp_u.get(), half_width, temp_v.get(),
-          half_width, width, height);
-    } else {
-      // |va_fourcc| is YUY2 or YUYV, which are handled the same.
-      // TODO(crbug.com/868400): support other formats/planarities/pitches.
-      conversion_result = libyuv::YUY2ToI420(
-          decoded_image.planes[0].data, decoded_image.planes[0].stride,
-          temp_y.get(), width, temp_u.get(), half_width, temp_v.get(),
-          half_width, width, height);
-    }
-    if (conversion_result != 0) {
-      DLOG(ERROR) << "libyuv conversion error";
-      return false;
-    }
-
-    ssim = libyuv::I420Ssim(libyuv_y_plane.get(), width, libyuv_u_plane.get(),
-                            half_width, libyuv_v_plane.get(), half_width,
-                            temp_y.get(), width, temp_u.get(), half_width,
-                            temp_v.get(), half_width, width, height);
-  } else {
-    DLOG(ERROR) << "FourCC not supported: " << FourccToString(va_fourcc);
-    return false;
-  }
-
-  if (ssim < kMinSsim) {
-    DLOG(ERROR) << "SSIM too low: " << ssim << " < " << kMinSsim;
-    return false;
-  }
-
-  return true;
+  return sw_decoded_jpeg;
 }
 
 // Generates a checkerboard pattern as a JPEG image of a specified |size| and
@@ -337,9 +241,8 @@
 
 }  // namespace
 
-class VASurface;
-
-class VaapiJpegDecoderTest : public testing::TestWithParam<TestParam> {
+class VaapiJpegDecoderTest
+    : public testing::TestWithParam<vaapi_test_utils::TestParam> {
  protected:
   VaapiJpegDecoderTest() {
     const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
@@ -511,6 +414,16 @@
   nv12_format.fourcc = VA_FOURCC_NV12;
   EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(nv12_format));
 
+  // Decode the image using libyuv. Using |temp_*| for resource management.
+  std::vector<uint8_t> temp_y;
+  std::vector<uint8_t> temp_u;
+  std::vector<uint8_t> temp_v;
+  DecodedImagePtr sw_decoded_jpeg =
+      GetSwDecode(encoded_image, &temp_y, &temp_u, &temp_v);
+  ASSERT_TRUE(sw_decoded_jpeg);
+
+  // Now run the comparison between the sw and hw image decodes for the
+  // supported formats.
   for (const auto& image_format : supported_image_formats) {
     std::unique_ptr<ScopedVAImage> scoped_image =
         Decode(encoded_image, image_format.fourcc);
@@ -529,8 +442,10 @@
     if (actual_fourcc == VA_FOURCC_I420 || actual_fourcc == VA_FOURCC_NV12 ||
         actual_fourcc == VA_FOURCC_YUY2 ||
         actual_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
-      ASSERT_TRUE(CompareImages(
-          encoded_image, ScopedVAImageToDecodedVAImage(scoped_image.get())));
+      ASSERT_TRUE(vaapi_test_utils::CompareImages(
+          *sw_decoded_jpeg,
+          vaapi_test_utils::ScopedVAImageToDecodedImage(scoped_image.get()),
+          kMinSsim));
     }
     DVLOG(1) << "Got a " << FourccToString(scoped_image->image()->format.fourcc)
              << " VAImage (preferred " << FourccToString(image_format.fourcc)
@@ -582,8 +497,19 @@
         << "Decode unexpectedly failed for size = " << test_size.ToString();
     ASSERT_TRUE(decoder_.GetScopedVASurface());
     EXPECT_TRUE(decoder_.GetScopedVASurface()->IsValid());
-    EXPECT_TRUE(CompareImages(
-        jpeg_data_span, ScopedVAImageToDecodedVAImage(scoped_image.get())))
+
+    // Decode the image using libyuv. Using |temp_*| for resource management.
+    std::vector<uint8_t> temp_y;
+    std::vector<uint8_t> temp_u;
+    std::vector<uint8_t> temp_v;
+    DecodedImagePtr sw_decoded_jpeg =
+        GetSwDecode(jpeg_data_span, &temp_y, &temp_u, &temp_v);
+    ASSERT_TRUE(sw_decoded_jpeg);
+
+    EXPECT_TRUE(vaapi_test_utils::CompareImages(
+        *sw_decoded_jpeg,
+        vaapi_test_utils::ScopedVAImageToDecodedImage(scoped_image.get()),
+        kMinSsim))
         << "The SSIM check unexpectedly failed for size = "
         << test_size.ToString();
   }
@@ -642,19 +568,19 @@
                                              pixmap->GetBufferFormat());
   ASSERT_TRUE(gpu_memory_buffer);
   ASSERT_TRUE(gpu_memory_buffer->Map());
-  DecodedVAImage decoded_image{};
+  vaapi_test_utils::DecodedImage decoded_image{};
   const gfx::BufferFormat format = gpu_memory_buffer->GetFormat();
   if (format == gfx::BufferFormat::YVU_420) {
-    decoded_image.va_fourcc = VA_FOURCC_I420;
+    decoded_image.fourcc = VA_FOURCC_I420;
     decoded_image.number_of_planes = 3u;
   } else if (format == gfx::BufferFormat::YUV_420_BIPLANAR) {
-    decoded_image.va_fourcc = VA_FOURCC_NV12;
+    decoded_image.fourcc = VA_FOURCC_NV12;
     decoded_image.number_of_planes = 2u;
   } else {
     ASSERT_TRUE(false) << "Unsupported format "
                        << gfx::BufferFormatToString(format);
   }
-  decoded_image.coded_size = gpu_memory_buffer->GetSize();
+  decoded_image.size = gpu_memory_buffer->GetSize();
   for (size_t plane = 0u;
        plane < base::strict_cast<size_t>(decoded_image.number_of_planes);
        plane++) {
@@ -662,7 +588,17 @@
         static_cast<uint8_t*>(gpu_memory_buffer->memory(plane));
     decoded_image.planes[plane].stride = gpu_memory_buffer->stride(plane);
   }
-  EXPECT_TRUE(CompareImages(encoded_image, decoded_image));
+
+  // Decode the image using libyuv. Using |temp_*| for resource management.
+  std::vector<uint8_t> temp_y;
+  std::vector<uint8_t> temp_u;
+  std::vector<uint8_t> temp_v;
+  DecodedImagePtr sw_decoded_jpeg =
+      GetSwDecode(encoded_image, &temp_y, &temp_u, &temp_v);
+  ASSERT_TRUE(sw_decoded_jpeg);
+
+  EXPECT_TRUE(vaapi_test_utils::CompareImages(*sw_decoded_jpeg, decoded_image,
+                                              kMinSsim));
   gpu_memory_buffer->Unmap();
 }
 
@@ -785,14 +721,9 @@
   EXPECT_FALSE(decoder_.GetScopedVASurface());
 }
 
-std::string TestParamToString(
-    const testing::TestParamInfo<TestParam>& param_info) {
-  return param_info.param.test_name;
-}
-
 INSTANTIATE_TEST_SUITE_P(,
                          VaapiJpegDecoderTest,
                          testing::ValuesIn(kTestCases),
-                         TestParamToString);
+                         vaapi_test_utils::TestParamToString);
 
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index c6c7ce2..2f6c679 100644
--- a/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -5,11 +5,10 @@
 #include "media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.h"
 
 #include <stddef.h>
+#include <va/va.h>
 
 #include <utility>
 
-#include <va/va.h>
-
 #include "base/bind.h"
 #include "base/containers/span.h"
 #include "base/location.h"
@@ -282,7 +281,7 @@
 }
 
 bool VaapiMjpegDecodeAccelerator::IsSupported() {
-  return VaapiWrapper::IsJpegDecodeSupported();
+  return VaapiWrapper::IsDecodeSupported(VAProfileJPEGBaseline);
 }
 
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_utils.cc b/media/gpu/vaapi/vaapi_utils.cc
index 8d88cc1..03e625e 100644
--- a/media/gpu/vaapi/vaapi_utils.cc
+++ b/media/gpu/vaapi/vaapi_utils.cc
@@ -4,11 +4,11 @@
 
 #include "media/gpu/vaapi/vaapi_utils.h"
 
+#include <va/va.h>
+
 #include <type_traits>
 #include <utility>
 
-#include <va/va.h>
-
 #include "base/logging.h"
 #include "base/numerics/ranges.h"
 #include "base/synchronization/lock.h"
diff --git a/media/gpu/vaapi/vaapi_utils_unittest.cc b/media/gpu/vaapi/vaapi_utils_unittest.cc
index d4821363..fdf8e54 100644
--- a/media/gpu/vaapi/vaapi_utils_unittest.cc
+++ b/media/gpu/vaapi/vaapi_utils_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <va/va.h>
+
 #include <memory>
 #include <vector>
 
-#include <va/va.h>
-
 // This has to be included first.
 // See http://code.google.com/p/googletest/issues/detail?id=371
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index c4831ed..95df449 100644
--- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -5,11 +5,10 @@
 #include "media/gpu/vaapi/vaapi_video_decode_accelerator.h"
 
 #include <string.h>
+#include <va/va.h>
 
 #include <memory>
 
-#include <va/va.h>
-
 #include "base/bind.h"
 #include "base/cpu.h"
 #include "base/files/scoped_file.h"
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 70156c8..122bfb1 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -5,16 +5,15 @@
 #include "media/gpu/vaapi/vaapi_video_encode_accelerator.h"
 
 #include <string.h>
+#include <va/va.h>
+#include <va/va_enc_h264.h>
+#include <va/va_enc_vp8.h>
 
 #include <algorithm>
 #include <memory>
 #include <type_traits>
 #include <utility>
 
-#include <va/va.h>
-#include <va/va_enc_h264.h>
-#include <va/va_enc_vp8.h>
-
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
diff --git a/media/gpu/vaapi/vaapi_webp_decoder.cc b/media/gpu/vaapi/vaapi_webp_decoder.cc
new file mode 100644
index 0000000..8b11273
--- /dev/null
+++ b/media/gpu/vaapi/vaapi_webp_decoder.cc
@@ -0,0 +1,129 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/vaapi/vaapi_webp_decoder.h"
+
+#include <va/va.h>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "media/gpu/macros.h"
+#include "media/gpu/vaapi/vaapi_utils.h"
+#include "media/gpu/vaapi/vaapi_wrapper.h"
+#include "media/gpu/vp8_reference_frame_vector.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/parsers/webp_parser.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+namespace {
+
+constexpr VAProfile kWebPVAProfile = VAProfileVP8Version0_3;
+constexpr unsigned int kWebPVARtFormat = VA_RT_FORMAT_YUV420;
+
+static bool IsVaapiSupportedWebP(const Vp8FrameHeader& webp_header) {
+  if (!VaapiWrapper::IsDecodingSupportedForInternalFormat(kWebPVAProfile,
+                                                          kWebPVARtFormat)) {
+    DLOG(ERROR) << "The WebP's subsampling format is unsupported";
+    return false;
+  }
+
+  // Validate the size.
+  // TODO(crbug.com/984971): Make sure visible size and coded size are treated
+  // similarly here: we don't currently know if we really have to provide the
+  // coded size to the VAAPI. So far, it seems to work by just passing the
+  // visible size, but we have to learn more, probably by looking into the
+  // driver. If we need to pass the coded size, then when checking against the
+  // min/max resolutions, we should use the coded size and not the visible size.
+  const gfx::Size webp_size(base::strict_cast<int>(webp_header.width),
+                            base::strict_cast<int>(webp_header.height));
+  if (webp_size.IsEmpty()) {
+    DLOG(ERROR) << "Width or height cannot be zero: " << webp_size.ToString();
+    return false;
+  }
+
+  gfx::Size min_webp_resolution;
+  if (!VaapiWrapper::GetDecodeMinResolution(kWebPVAProfile,
+                                            &min_webp_resolution)) {
+    DLOG(ERROR) << "Could not get the minimum resolution";
+    return false;
+  }
+  if (webp_size.width() < min_webp_resolution.width() ||
+      webp_size.height() < min_webp_resolution.height()) {
+    DLOG(ERROR) << "VAAPI doesn't support size " << webp_size.ToString()
+                << ": under minimum resolution "
+                << min_webp_resolution.ToString();
+    return false;
+  }
+
+  gfx::Size max_webp_resolution;
+  if (!VaapiWrapper::GetDecodeMaxResolution(kWebPVAProfile,
+                                            &max_webp_resolution)) {
+    DLOG(ERROR) << "Could not get the maximum resolution";
+    return false;
+  }
+  if (webp_size.width() > max_webp_resolution.width() ||
+      webp_size.height() > max_webp_resolution.height()) {
+    DLOG(ERROR) << "VAAPI doesn't support size " << webp_size.ToString()
+                << ": above maximum resolution "
+                << max_webp_resolution.ToString();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+VaapiWebPDecoder::VaapiWebPDecoder() : VaapiImageDecoder(kWebPVAProfile) {}
+
+VaapiWebPDecoder::~VaapiWebPDecoder() = default;
+
+gpu::ImageDecodeAcceleratorType VaapiWebPDecoder::GetType() const {
+  return gpu::ImageDecodeAcceleratorType::kWebP;
+}
+
+VaapiImageDecodeStatus VaapiWebPDecoder::AllocateVASurfaceAndSubmitVABuffers(
+    base::span<const uint8_t> encoded_image) {
+  DCHECK(vaapi_wrapper_);
+  std::unique_ptr<Vp8FrameHeader> parse_result = ParseWebPImage(encoded_image);
+  if (!parse_result)
+    return VaapiImageDecodeStatus::kParseFailed;
+
+  // Make sure this WebP can be decoded.
+  if (!IsVaapiSupportedWebP(*parse_result))
+    return VaapiImageDecodeStatus::kUnsupportedImage;
+
+  // Prepare the VaSurface for decoding.
+  const gfx::Size new_visible_size(
+      base::strict_cast<int>(parse_result->width),
+      base::strict_cast<int>(parse_result->height));
+  DCHECK(!scoped_va_context_and_surface_ ||
+         scoped_va_context_and_surface_->IsValid());
+  DCHECK(!scoped_va_context_and_surface_ ||
+         (scoped_va_context_and_surface_->format() == kWebPVARtFormat));
+  if (!scoped_va_context_and_surface_ ||
+      new_visible_size != scoped_va_context_and_surface_->size()) {
+    scoped_va_context_and_surface_.reset();
+    scoped_va_context_and_surface_ = ScopedVAContextAndSurface(
+        vaapi_wrapper_
+            ->CreateContextAndScopedVASurface(kWebPVARtFormat, new_visible_size)
+            .release());
+    if (!scoped_va_context_and_surface_) {
+      VLOGF(1) << "CreateContextAndScopedVASurface() failed";
+      return VaapiImageDecodeStatus::kSurfaceCreationFailed;
+    }
+    DCHECK(scoped_va_context_and_surface_->IsValid());
+  }
+
+  if (!FillVP8DataStructures(vaapi_wrapper_,
+                             scoped_va_context_and_surface_->id(),
+                             *parse_result, Vp8ReferenceFrameVector())) {
+    return VaapiImageDecodeStatus::kSubmitVABuffersFailed;
+  }
+
+  return VaapiImageDecodeStatus::kSuccess;
+}
+
+}  // namespace media
diff --git a/media/gpu/vaapi/vaapi_webp_decoder.h b/media/gpu/vaapi/vaapi_webp_decoder.h
new file mode 100644
index 0000000..ec0647b
--- /dev/null
+++ b/media/gpu/vaapi/vaapi_webp_decoder.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_VAAPI_VAAPI_WEBP_DECODER_H_
+#define MEDIA_GPU_VAAPI_VAAPI_WEBP_DECODER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "media/gpu/vaapi/vaapi_image_decoder.h"
+
+namespace media {
+
+class VaapiWebPDecoder : public VaapiImageDecoder {
+ public:
+  VaapiWebPDecoder();
+  ~VaapiWebPDecoder() override;
+
+  // VaapiImageDecoder implementation.
+  gpu::ImageDecodeAcceleratorType GetType() const override;
+
+ private:
+  // VaapiImageDecoder implementation.
+  VaapiImageDecodeStatus AllocateVASurfaceAndSubmitVABuffers(
+      base::span<const uint8_t> encoded_image) override;
+
+  DISALLOW_COPY_AND_ASSIGN(VaapiWebPDecoder);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_VAAPI_VAAPI_WEBP_DECODER_H_
diff --git a/media/gpu/vaapi/vaapi_webp_decoder_unittest.cc b/media/gpu/vaapi/vaapi_webp_decoder_unittest.cc
new file mode 100644
index 0000000..88fce45a
--- /dev/null
+++ b/media/gpu/vaapi/vaapi_webp_decoder_unittest.cc
@@ -0,0 +1,220 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <va/va.h>
+
+#include <memory>
+#include <string>
+
+// This has to be included first.
+// See http://code.google.com/p/googletest/issues/detail?id=371
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/containers/span.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/numerics/safe_conversions.h"
+#include "media/base/test_data_util.h"
+#include "media/capture/video/chromeos/local_gpu_memory_buffer_manager.h"
+#include "media/gpu/vaapi/test_utils.h"
+#include "media/gpu/vaapi/vaapi_image_decoder.h"
+#include "media/gpu/vaapi/vaapi_utils.h"
+#include "media/gpu/vaapi/vaapi_webp_decoder.h"
+#include "media/gpu/vaapi/vaapi_wrapper.h"
+#include "media/parsers/vp8_parser.h"
+#include "media/parsers/webp_parser.h"
+#include "third_party/libwebp/src/webp/decode.h"
+#include "ui/gfx/buffer_types.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gpu_memory_buffer.h"
+#include "ui/gfx/linux/native_pixmap_dmabuf.h"
+#include "ui/gfx/native_pixmap_handle.h"
+
+namespace media {
+namespace {
+
+constexpr const char* kSmallImageFilename =
+    "RGB_noise_large_pixels_115x115.webp";
+constexpr const char* kMediumImageFilename =
+    "RGB_noise_large_pixels_2015x2015.webp";
+constexpr const char* kLargeImageFilename =
+    "RGB_noise_large_pixels_4000x4000.webp";
+
+constexpr const char* kLowFreqImageFilename = "solid_green_2015x2015.webp";
+constexpr const char* kMedFreqImageFilename =
+    "BlackAndWhite_criss-cross_pattern_2015x2015.webp";
+constexpr const char* kHighFreqImageFilename = "RGB_noise_2015x2015.webp";
+
+const vaapi_test_utils::TestParam kTestCases[] = {
+    {"SmallImage_115x115", kSmallImageFilename},
+    {"MediumImage_2015x2015", kMediumImageFilename},
+    {"LargeImage_4000x4000", kLargeImageFilename},
+    {"LowFreqImage", kLowFreqImageFilename},
+    {"MedFreqImage", kMedFreqImageFilename},
+    {"HighFreqImage", kHighFreqImageFilename},
+};
+
+// Any number above 99.5% should do. We are being very aggressive here.
+constexpr double kMinSsim = 0.999;
+
+// WebpDecode*() returns memory that has to be released in a specific way.
+struct WebpDecodeDeleter {
+  void operator()(void* ptr) { WebPFree(ptr); }
+};
+
+}  // namespace
+
+class VaapiWebPDecoderTest
+    : public testing::TestWithParam<vaapi_test_utils::TestParam> {
+ protected:
+  VaapiWebPDecoderTest() {
+    const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+    if (cmd_line && cmd_line->HasSwitch("test_data_path"))
+      test_data_path_ = cmd_line->GetSwitchValueASCII("test_data_path");
+  }
+
+  void SetUp() override {
+    if (!VaapiWrapper::IsDecodeSupported(VAProfileVP8Version0_3)) {
+      DLOG(INFO) << "VP8 decoding is not supported by the VA-API.";
+      GTEST_SKIP();
+    }
+
+    ASSERT_TRUE(decoder_.Initialize(base::BindRepeating(
+        []() { LOG(FATAL) << "Oh noes! Decoder failed"; })));
+  }
+
+  // Find the location of the specified test file. If a file with specified path
+  // is not found, treat the file as being relative to the test file directory.
+  // This is either a custom test data path provided by --test_data_path, or the
+  // default test data path (//media/test/data).
+  base::FilePath FindTestDataFilePath(const std::string& file_name) {
+    const base::FilePath file_path = base::FilePath(file_name);
+    if (base::PathExists(file_path))
+      return file_path;
+    if (!test_data_path_.empty())
+      return base::FilePath(test_data_path_).Append(file_path);
+    return GetTestDataFilePath(file_name);
+  }
+
+  scoped_refptr<gfx::NativePixmapDmaBuf> DecodeToNativePixmapDmaBuf(
+      base::span<const uint8_t> encoded_image,
+      VaapiImageDecodeStatus* status = nullptr) {
+    const VaapiImageDecodeStatus decode_status = decoder_.Decode(encoded_image);
+    EXPECT_EQ(!!decoder_.GetScopedVASurface(),
+              decode_status == VaapiImageDecodeStatus::kSuccess);
+
+    // Still try to get the pixmap when decode fails.
+    VaapiImageDecodeStatus pixmap_status;
+    scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
+        decoder_.ExportAsNativePixmapDmaBuf(&pixmap_status);
+    EXPECT_EQ(!!pixmap, pixmap_status == VaapiImageDecodeStatus::kSuccess);
+
+    // Return the first fail status.
+    if (status) {
+      *status = decode_status != VaapiImageDecodeStatus::kSuccess
+                    ? decode_status
+                    : pixmap_status;
+    }
+    return pixmap;
+  }
+
+ protected:
+  std::string test_data_path_;
+  VaapiWebPDecoder decoder_;
+};
+
+TEST_P(VaapiWebPDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) {
+  base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
+  std::string webp_data;
+  ASSERT_TRUE(base::ReadFileToString(input_file, &webp_data))
+      << "failed to read input data from " << input_file.value();
+  const auto encoded_image = base::make_span<const uint8_t>(
+      reinterpret_cast<const uint8_t*>(webp_data.data()), webp_data.size());
+
+  // Decode the image using the VA-API and wrap the decoded image in a
+  // DecodedImage object.
+  ASSERT_TRUE(VaapiWrapper::IsDecodingSupportedForInternalFormat(
+      VAProfileVP8Version0_3, VA_RT_FORMAT_YUV420));
+
+  VaapiImageDecodeStatus status;
+  scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
+      DecodeToNativePixmapDmaBuf(encoded_image, &status);
+  ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, status);
+  EXPECT_FALSE(decoder_.GetScopedVASurface());
+  ASSERT_TRUE(pixmap);
+  ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, pixmap->GetBufferFormat());
+
+  gfx::NativePixmapHandle handle = pixmap->ExportHandle();
+  LocalGpuMemoryBufferManager gpu_memory_buffer_manager;
+  std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
+      gpu_memory_buffer_manager.ImportDmaBuf(handle, pixmap->GetBufferSize(),
+                                             pixmap->GetBufferFormat());
+  ASSERT_TRUE(gpu_memory_buffer);
+  ASSERT_TRUE(gpu_memory_buffer->Map());
+  ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR,
+            gpu_memory_buffer->GetFormat());
+
+  vaapi_test_utils::DecodedImage hw_decoded_webp{};
+  hw_decoded_webp.fourcc = VA_FOURCC_NV12;
+  hw_decoded_webp.number_of_planes = 2u;
+  hw_decoded_webp.size = gpu_memory_buffer->GetSize();
+  for (size_t plane = 0u;
+       plane < base::strict_cast<size_t>(hw_decoded_webp.number_of_planes);
+       plane++) {
+    hw_decoded_webp.planes[plane].data =
+        static_cast<uint8_t*>(gpu_memory_buffer->memory(plane));
+    hw_decoded_webp.planes[plane].stride = gpu_memory_buffer->stride(plane);
+  }
+
+  // Decode the image using libwebp and wrap the decoded image in a
+  // DecodedImage object.
+  std::unique_ptr<Vp8FrameHeader> parse_result = ParseWebPImage(encoded_image);
+  ASSERT_TRUE(parse_result);
+
+  int reference_width;
+  int reference_height;
+  int y_stride;
+  int uv_stride;
+  uint8_t* libwebp_u_plane = nullptr;
+  uint8_t* libwebp_v_plane = nullptr;
+  std::unique_ptr<uint8_t, WebpDecodeDeleter> libwebp_y_plane(
+      WebPDecodeYUV(encoded_image.data(), encoded_image.size(),
+                    &reference_width, &reference_height, &libwebp_u_plane,
+                    &libwebp_v_plane, &y_stride, &uv_stride));
+  ASSERT_TRUE(libwebp_y_plane && libwebp_u_plane && libwebp_v_plane);
+  ASSERT_EQ(reference_width, base::strict_cast<int>(parse_result->width));
+  ASSERT_EQ(reference_height, base::strict_cast<int>(parse_result->height));
+
+  // Wrap the software decoded image in a DecodedImage object.
+  vaapi_test_utils::DecodedImage sw_decoded_webp{};
+  sw_decoded_webp.fourcc = VA_FOURCC_I420;
+  sw_decoded_webp.number_of_planes = 3u;
+  sw_decoded_webp.size = gfx::Size(reference_width, reference_height);
+  sw_decoded_webp.planes[0].data = libwebp_y_plane.get();
+  sw_decoded_webp.planes[0].stride = y_stride;
+  sw_decoded_webp.planes[1].data = libwebp_u_plane;
+  sw_decoded_webp.planes[1].stride = uv_stride;
+  sw_decoded_webp.planes[2].data = libwebp_v_plane;
+  sw_decoded_webp.planes[2].stride = uv_stride;
+
+  EXPECT_TRUE(vaapi_test_utils::CompareImages(sw_decoded_webp, hw_decoded_webp,
+                                              kMinSsim));
+  gpu_memory_buffer->Unmap();
+}
+
+// TODO(crbug.com/986073): expand test coverage. See
+// vaapi_jpeg_decoder_unittest.cc as reference:
+// cs.chromium.org/chromium/src/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc
+INSTANTIATE_TEST_SUITE_P(,
+                         VaapiWebPDecoderTest,
+                         testing::ValuesIn(kTestCases),
+                         vaapi_test_utils::TestParamToString);
+
+}  // namespace media
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index fc68b50..7df60e2 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -7,16 +7,15 @@
 #include <dlfcn.h>
 #include <string.h>
 #include <unistd.h>
-
-#include <algorithm>
-#include <type_traits>
-#include <utility>
-
 #include <va/va.h>
 #include <va/va_drm.h>
 #include <va/va_drmcommon.h>
 #include <va/va_version.h>
 
+#include <algorithm>
+#include <type_traits>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
@@ -1123,9 +1122,8 @@
 }
 
 // static
-bool VaapiWrapper::IsJpegDecodeSupported() {
-  return VASupportedProfiles::Get().IsProfileSupported(kDecode,
-                                                       VAProfileJPEGBaseline);
+bool VaapiWrapper::IsDecodeSupported(VAProfile va_profile) {
+  return VASupportedProfiles::Get().IsProfileSupported(kDecode, va_profile);
 }
 
 // static
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h
index 82950b26..4d6c0d0 100644
--- a/media/gpu/vaapi/vaapi_wrapper.h
+++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -12,13 +12,12 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <va/va.h>
 
 #include <set>
 #include <string>
 #include <vector>
 
-#include <va/va.h>
-
 #include "base/files/file.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
@@ -101,8 +100,8 @@
   // Return the supported video decode profiles.
   static VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles();
 
-  // Return true when JPEG decode is supported.
-  static bool IsJpegDecodeSupported();
+  // Return true when decoding using |va_profile| is supported.
+  static bool IsDecodeSupported(VAProfile va_profile);
 
   // Returns the supported internal formats for decoding using |va_profile|. If
   // decoding is not supported for that profile, returns InternalFormats{}.
diff --git a/media/gpu/windows/supported_profile_helpers.cc b/media/gpu/windows/supported_profile_helpers.cc
index b7b24694..2732a38 100644
--- a/media/gpu/windows/supported_profile_helpers.cc
+++ b/media/gpu/windows/supported_profile_helpers.cc
@@ -164,7 +164,6 @@
     const std::vector<GUID>& valid_guids,
     const std::vector<gfx::Size>& resolutions_to_test,
     DXGI_FORMAT format) {
-  TRACE_EVENT0("gpu,startup", "GetMaxResolutionsForGUIDs");
   ResolutionPair result(default_max, gfx::Size());
 
   // Enumerate supported video profiles and look for the profile.
@@ -208,12 +207,15 @@
   return result;
 }
 
+// TODO(tmathmeyer) refactor this so that we don'ty call
+// GetMaxResolutionsForGUIDS so many times.
 void GetResolutionsForDecoders(std::vector<GUID> h264_guids,
                                ComD3D11Device device,
                                const gpu::GpuDriverBugWorkarounds& workarounds,
                                ResolutionPair* h264_resolutions,
                                ResolutionPair* vp9_0_resolutions,
                                ResolutionPair* vp9_2_resolutions) {
+  TRACE_EVENT0("gpu,startup", "GetResolutionsForDecoders");
   if (base::win::GetVersion() > base::win::Version::WIN7) {
     // To detect if a driver supports the desired resolutions, we try and create
     // a DXVA decoder instance for that resolution and profile. If that succeeds
diff --git a/media/gpu/windows/supported_profile_helpers.h b/media/gpu/windows/supported_profile_helpers.h
index ae2a6a8..9c0d2d9 100644
--- a/media/gpu/windows/supported_profile_helpers.h
+++ b/media/gpu/windows/supported_profile_helpers.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "media/gpu/media_gpu_export.h"
 #include "media/gpu/windows/d3d11_com_defs.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -37,6 +38,7 @@
     const std::vector<gfx::Size>& resolutions_to_test,
     DXGI_FORMAT format = DXGI_FORMAT_NV12);
 
+MEDIA_GPU_EXPORT
 void GetResolutionsForDecoders(std::vector<GUID> h264_guids,
                                ComD3D11Device device,
                                const gpu::GpuDriverBugWorkarounds& workarounds,
diff --git a/media/gpu/windows/supported_profile_helpers_unittest.cc b/media/gpu/windows/supported_profile_helpers_unittest.cc
new file mode 100644
index 0000000..206871a8
--- /dev/null
+++ b/media/gpu/windows/supported_profile_helpers_unittest.cc
@@ -0,0 +1,234 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/windows/supported_profile_helpers.h"
+
+#include <d3d11.h>
+#include <d3d11_1.h>
+#include <initguid.h>
+#include <map>
+#include <utility>
+
+#include "base/win/windows_version.h"
+#include "media/base/test_helpers.h"
+#include "media/base/win/d3d11_mocks.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::WithArgs;
+
+#define DONT_RUN_ON_WIN_7()                                  \
+  do {                                                       \
+    if (base::win::GetVersion() <= base::win::Version::WIN7) \
+      return;                                                \
+  } while (0)
+
+HRESULT SetIfSizeLessThan(D3D11_VIDEO_DECODER_DESC* desc, UINT* count) {
+  *count = 1;
+  return S_OK;
+}
+
+namespace media {
+
+class SupportedResolutionResolverTest : public ::testing::Test {
+ public:
+  const std::pair<uint16_t, uint16_t> LegacyIntelGPU = {0x8086, 0x102};
+  const std::pair<uint16_t, uint16_t> RecentIntelGPU = {0x8086, 0x100};
+  const std::pair<uint16_t, uint16_t> LegacyAMDGPU = {0x1022, 0x130f};
+  const std::pair<uint16_t, uint16_t> RecentAMDGPU = {0x1022, 0x130e};
+
+  const ResolutionPair ten_eighty = {{1920, 1080}, {1080, 1920}};
+  const ResolutionPair zero = {{0, 0}, {0, 0}};
+  const ResolutionPair tall4k = {{4096, 2304}, {2304, 4096}};
+  const ResolutionPair eightKsquare = {{8192, 8192}, {8192, 8192}};
+
+  void SetUp() override {
+    gpu_workarounds_.disable_dxgi_zero_copy_video = false;
+    mock_d3d11_device_ = CreateD3D11Mock<NiceMock<D3D11DeviceMock>>();
+
+    mock_dxgi_device_ = CreateD3D11Mock<NiceMock<DXGIDeviceMock>>();
+    ON_CALL(*mock_d3d11_device_.Get(), QueryInterface(IID_IDXGIDevice, _))
+        .WillByDefault(SetComPointeeAndReturnOk<1>(mock_dxgi_device_.Get()));
+
+    mock_d3d11_video_device_ =
+        CreateD3D11Mock<NiceMock<D3D11VideoDeviceMock>>();
+    ON_CALL(*mock_d3d11_device_.Get(), QueryInterface(IID_ID3D11VideoDevice, _))
+        .WillByDefault(
+            SetComPointeeAndReturnOk<1>(mock_d3d11_video_device_.Get()));
+
+    mock_dxgi_adapter_ = CreateD3D11Mock<NiceMock<DXGIAdapterMock>>();
+    ON_CALL(*mock_dxgi_device_.Get(), GetAdapter(_))
+        .WillByDefault(SetComPointeeAndReturnOk<0>(mock_dxgi_adapter_.Get()));
+
+    SetGPUProfile(RecentIntelGPU);
+    SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_H264_VLD_NOFGT, {4096, 4096});
+  }
+
+  void SetMaxResolutionForGUID(const GUID& g, const gfx::Size& max_res) {
+    max_size_for_guids_[g] = max_res;
+    ON_CALL(*mock_d3d11_video_device_.Get(), GetVideoDecoderConfigCount(_, _))
+        .WillByDefault(
+            WithArgs<0, 1>(Invoke([this](const D3D11_VIDEO_DECODER_DESC* desc,
+                                         UINT* count) -> HRESULT {
+              *count = 0;
+              const auto& itr = this->max_size_for_guids_.find(desc->Guid);
+              if (itr == this->max_size_for_guids_.end())
+                return E_FAIL;
+              const gfx::Size max = itr->second;
+              if (max.height() < 0 || max.width() < 0)
+                return E_FAIL;
+              if (static_cast<UINT>(max.height()) < desc->SampleHeight)
+                return E_FAIL;
+              if (static_cast<UINT>(max.width()) < desc->SampleWidth)
+                return S_OK;
+              *count = 1;
+              return S_OK;
+            })));
+  }
+
+  void EnableDecoders(const std::vector<GUID>& decoder_guids) {
+    ON_CALL(*mock_d3d11_video_device_.Get(), GetVideoDecoderProfileCount())
+        .WillByDefault(Return(decoder_guids.size()));
+
+    // Note that we don't check if the guid in the config actually matches
+    // |decoder_profile|.  Perhaps we should.
+    ON_CALL(*mock_d3d11_video_device_.Get(), GetVideoDecoderProfile(_, _))
+        .WillByDefault(WithArgs<0, 1>(
+            Invoke([decoder_guids](UINT p_idx, GUID* guid) -> HRESULT {
+              if (p_idx >= decoder_guids.size())
+                return E_FAIL;
+              *guid = decoder_guids.at(p_idx);
+              return S_OK;
+            })));
+  }
+
+  void SetGPUProfile(std::pair<uint16_t, uint16_t> vendor_and_gpu) {
+    mock_adapter_desc_.DeviceId = static_cast<UINT>(vendor_and_gpu.second);
+    mock_adapter_desc_.VendorId = static_cast<UINT>(vendor_and_gpu.first);
+
+    ON_CALL(*mock_dxgi_adapter_.Get(), GetDesc(_))
+        .WillByDefault(
+            DoAll(SetArgPointee<0>(mock_adapter_desc_), Return(S_OK)));
+  }
+
+  Microsoft::WRL::ComPtr<D3D11DeviceMock> mock_d3d11_device_;
+  Microsoft::WRL::ComPtr<DXGIAdapterMock> mock_dxgi_adapter_;
+  Microsoft::WRL::ComPtr<DXGIDeviceMock> mock_dxgi_device_;
+  Microsoft::WRL::ComPtr<D3D11VideoDeviceMock> mock_d3d11_video_device_;
+  DXGI_ADAPTER_DESC mock_adapter_desc_;
+  gpu::GpuDriverBugWorkarounds gpu_workarounds_;
+
+  struct GUIDComparison {
+    bool operator()(const GUID& a, const GUID& b) const {
+      return memcmp(&a, &b, sizeof(GUID)) < 0;
+    }
+  };
+  std::map<GUID, gfx::Size, GUIDComparison> max_size_for_guids_;
+};
+
+TEST_F(SupportedResolutionResolverTest, NoDeviceAllDefault) {
+  DONT_RUN_ON_WIN_7();
+
+  ResolutionPair h264_res_expected = {{1, 2}, {3, 4}};
+  ResolutionPair h264_res = {{1, 2}, {3, 4}};
+  ResolutionPair vp9_0_res;
+  ResolutionPair vp9_2_res;
+  GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT}, nullptr,
+                            gpu_workarounds_, &h264_res, &vp9_0_res,
+                            &vp9_2_res);
+
+  ASSERT_EQ(h264_res, h264_res_expected);
+  ASSERT_EQ(vp9_0_res, zero);
+  ASSERT_EQ(vp9_0_res, zero);
+}
+
+TEST_F(SupportedResolutionResolverTest, LegacyGPUAllDefault) {
+  DONT_RUN_ON_WIN_7();
+
+  SetGPUProfile(LegacyIntelGPU);
+
+  ResolutionPair h264_res_expected = {{1, 2}, {3, 4}};
+  ResolutionPair h264_res = {{1, 2}, {3, 4}};
+  ResolutionPair vp9_0_res;
+  ResolutionPair vp9_2_res;
+  GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT},
+                            mock_d3d11_device_, gpu_workarounds_, &h264_res,
+                            &vp9_0_res, &vp9_2_res);
+
+  ASSERT_EQ(h264_res, h264_res_expected);
+  ASSERT_EQ(vp9_2_res, zero);
+  ASSERT_EQ(vp9_0_res, zero);
+}
+
+TEST_F(SupportedResolutionResolverTest, WorkaroundsDisableVpx) {
+  DONT_RUN_ON_WIN_7();
+
+  gpu_workarounds_.disable_dxgi_zero_copy_video = true;
+  EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT});
+
+  ResolutionPair h264_res;
+  ResolutionPair vp9_0_res;
+  ResolutionPair vp9_2_res;
+  GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT},
+                            mock_d3d11_device_, gpu_workarounds_, &h264_res,
+                            &vp9_0_res, &vp9_2_res);
+
+  ASSERT_EQ(h264_res, tall4k);
+
+  ASSERT_EQ(vp9_0_res, zero);
+
+  ASSERT_EQ(vp9_2_res, zero);
+}
+
+TEST_F(SupportedResolutionResolverTest, VP9_0Supports8k) {
+  DONT_RUN_ON_WIN_7();
+
+  EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT,
+                  D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0});
+  SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, {8192, 8192});
+
+  ResolutionPair h264_res;
+  ResolutionPair vp9_0_res;
+  ResolutionPair vp9_2_res;
+  GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT},
+                            mock_d3d11_device_, gpu_workarounds_, &h264_res,
+                            &vp9_0_res, &vp9_2_res);
+
+  ASSERT_EQ(h264_res, tall4k);
+
+  ASSERT_EQ(vp9_0_res, eightKsquare);
+
+  ASSERT_EQ(vp9_2_res, zero);
+}
+
+TEST_F(SupportedResolutionResolverTest, BothVP9ProfilesSupported) {
+  DONT_RUN_ON_WIN_7();
+
+  EnableDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT,
+                  D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0,
+                  D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2});
+  SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, {8192, 8192});
+  SetMaxResolutionForGUID(D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2,
+                          {8192, 8192});
+
+  ResolutionPair h264_res;
+  ResolutionPair vp9_0_res;
+  ResolutionPair vp9_2_res;
+  GetResolutionsForDecoders({D3D11_DECODER_PROFILE_H264_VLD_NOFGT},
+                            mock_d3d11_device_, gpu_workarounds_, &h264_res,
+                            &vp9_0_res, &vp9_2_res);
+
+  ASSERT_EQ(h264_res, tall4k);
+
+  ASSERT_EQ(vp9_0_res, eightKsquare);
+
+  ASSERT_EQ(vp9_2_res, eightKsquare);
+}
+
+}  // namespace media
diff --git a/media/test/data/BlackAndWhite_criss-cross_pattern_2015x2015.webp b/media/test/data/BlackAndWhite_criss-cross_pattern_2015x2015.webp
new file mode 100644
index 0000000..8520c36d
--- /dev/null
+++ b/media/test/data/BlackAndWhite_criss-cross_pattern_2015x2015.webp
Binary files differ
diff --git a/media/test/data/README.md b/media/test/data/README.md
index 984d114f..4ca8fbb2 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -819,12 +819,36 @@
 
 ### WebP Test Files
 
+#### BlackAndWhite_criss-cross_pattern_2015x2015.webp
+A lossy WebP encoded image of 2015x2015. Created by gildekel@ using Gimp.
+
 #### bouncy_ball.webp
-An animated (extended) WebP encoded image of 450x450. Created by gildekel@ using Gimp.
+An animated (extended) WebP encoded image of 450x450. Created by gildekel@
+using Gimp.
 
 #### red_green_gradient_lossy.webp
 A lossy WebP encoded image of 3000x3000. Created by gildekel@ using Gimp.
 
+#### RGB_noise_2015x2015.webp
+A lossy WebP encoded image of 2015x2015 that contains RGB noise. Created by
+gildekel@ using Gimp.
+
+#### RGB_noise_large_pixels_115x115.webp
+A lossy WebP encoded image of 115x115 that contains large pixels RGB noise.
+Created by gildekel@ using Gimp.
+
+#### RGB_noise_large_pixels_2015x2015.webp
+A lossy WebP encoded image of 2015x2015 that contains large pixels RGB noise.
+Created by gildekel@ using Gimp.
+
+#### RGB_noise_large_pixels_4000x4000.webp
+A lossy WebP encoded image of 4000x4000 that contains large pixels RGB noise.
+Created by gildekel@ using Gimp.
+
+#### solid_green_2015x2015.webp
+A lossy WebP encoded image of 2015x2015 of solid bright green. Created by
+gildekel@ using Gimp.
+
 #### yellow_pink_gradient_lossless.webp
 A lossless WebP encoded image of 3000x3000. Created by gildekel@ using Gimp.
 
diff --git a/media/test/data/RGB_noise_2015x2015.webp b/media/test/data/RGB_noise_2015x2015.webp
new file mode 100644
index 0000000..e9204e5
--- /dev/null
+++ b/media/test/data/RGB_noise_2015x2015.webp
Binary files differ
diff --git a/media/test/data/RGB_noise_large_pixels_115x115.webp b/media/test/data/RGB_noise_large_pixels_115x115.webp
new file mode 100644
index 0000000..cb5cdb4
--- /dev/null
+++ b/media/test/data/RGB_noise_large_pixels_115x115.webp
Binary files differ
diff --git a/media/test/data/RGB_noise_large_pixels_2015x2015.webp b/media/test/data/RGB_noise_large_pixels_2015x2015.webp
new file mode 100644
index 0000000..7df797d
--- /dev/null
+++ b/media/test/data/RGB_noise_large_pixels_2015x2015.webp
Binary files differ
diff --git a/media/test/data/RGB_noise_large_pixels_4000x4000.webp b/media/test/data/RGB_noise_large_pixels_4000x4000.webp
new file mode 100644
index 0000000..71b66ed
--- /dev/null
+++ b/media/test/data/RGB_noise_large_pixels_4000x4000.webp
Binary files differ
diff --git a/media/test/data/solid_green_2015x2015.webp b/media/test/data/solid_green_2015x2015.webp
new file mode 100644
index 0000000..ed79a21a
--- /dev/null
+++ b/media/test/data/solid_green_2015x2015.webp
Binary files differ
diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc
index 2fe6d5d..b85a00a 100644
--- a/net/quic/quic_chromium_client_session.cc
+++ b/net/quic/quic_chromium_client_session.cc
@@ -969,11 +969,23 @@
 void QuicChromiumClientSession::UpdateStreamPriority(
     quic::QuicStreamId id,
     spdy::SpdyPriority new_priority) {
-  if (headers_include_h2_stream_dependency_) {
+  if (headers_include_h2_stream_dependency_ ||
+      VersionHasStreamType(connection()->transport_version())) {
     auto updates = priority_dependency_state_.OnStreamUpdate(id, new_priority);
     for (auto update : updates) {
-      WritePriority(update.id, update.parent_stream_id, update.weight,
-                    update.exclusive);
+      if (!VersionHasStreamType(connection()->transport_version())) {
+        WritePriority(update.id, update.parent_stream_id, update.weight,
+                      update.exclusive);
+      } else {
+        quic::PriorityFrame frame;
+        frame.weight = update.weight;
+        frame.exclusive = update.exclusive;
+        frame.prioritized_element_id = update.id;
+        frame.prioritized_type = quic::REQUEST_STREAM;
+        frame.dependency_type = quic::REQUEST_STREAM;
+        frame.element_dependency_id = update.parent_stream_id;
+        WriteH3Priority(frame);
+      }
     }
   }
   quic::QuicSpdySession::UpdateStreamPriority(id, new_priority);
@@ -3030,7 +3042,8 @@
                                  weak_factory_.GetWeakPtr(), GURL(pushed_url)),
                              net_log_);
     }
-    if (headers_include_h2_stream_dependency_) {
+    if (headers_include_h2_stream_dependency_ ||
+        VersionHasStreamType(connection()->transport_version())) {
       // Even though the promised stream will not be created until after the
       // push promise headers are received, send a PRIORITY frame for the
       // promised stream ID. Send |kDefaultPriority| since that will be the
@@ -3041,7 +3054,18 @@
       bool exclusive = false;
       priority_dependency_state_.OnStreamCreation(
           promised_id, priority, &parent_stream_id, &weight, &exclusive);
-      WritePriority(promised_id, parent_stream_id, weight, exclusive);
+      if (!VersionHasStreamType(connection()->transport_version())) {
+        WritePriority(promised_id, parent_stream_id, weight, exclusive);
+      } else {
+        quic::PriorityFrame frame;
+        frame.weight = weight;
+        frame.exclusive = exclusive;
+        frame.prioritized_type = quic::PUSH_STREAM;
+        frame.prioritized_element_id = promised_id;
+        frame.dependency_type = quic::REQUEST_STREAM;
+        frame.element_dependency_id = parent_stream_id;
+        WriteH3Priority(frame);
+      }
     }
   }
   net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PUSH_PROMISE_RECEIVED,
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index f553454b..1ba2168 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -2270,8 +2270,9 @@
   AddWrite(ConstructInitialSettingsPacket());
 
   uint64_t client_packet_number = 2;
-  if (client_headers_include_h2_stream_dependency_ &&
-      version_.transport_version >= quic::QUIC_VERSION_43) {
+  if ((client_headers_include_h2_stream_dependency_ &&
+       version_.transport_version >= quic::QUIC_VERSION_43) ||
+      VersionHasStreamType(version_.transport_version)) {
     AddWrite(ConstructClientPriorityPacket(client_packet_number++,
                                            kIncludeVersion, promise_id_, 0,
                                            DEFAULT_PRIORITY));
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index c745b5a..2a24deba 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -6094,8 +6094,9 @@
           1, GetNthClientInitiatedBidirectionalStreamId(0),
           GetNthServerInitiatedUnidirectionalStreamId(0), false,
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
-  if (client_headers_include_h2_stream_dependency_ &&
-      version_.transport_version >= quic::QUIC_VERSION_43) {
+  if ((client_headers_include_h2_stream_dependency_ &&
+       version_.transport_version >= quic::QUIC_VERSION_43) ||
+      VersionHasStreamType(version_.transport_version)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
@@ -6183,8 +6184,9 @@
           1, GetNthClientInitiatedBidirectionalStreamId(0),
           GetNthServerInitiatedUnidirectionalStreamId(0), false,
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
-  if (client_headers_include_h2_stream_dependency_ &&
-      version_.transport_version >= quic::QUIC_VERSION_43) {
+  if ((client_headers_include_h2_stream_dependency_ &&
+       version_.transport_version >= quic::QUIC_VERSION_43) ||
+      VersionHasStreamType(version_.transport_version)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
@@ -6436,8 +6438,9 @@
           GetNthServerInitiatedUnidirectionalStreamId(0), false,
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
 
-  if (client_headers_include_h2_stream_dependency_ &&
-      version_.transport_version >= quic::QUIC_VERSION_43) {
+  if ((client_headers_include_h2_stream_dependency_ &&
+       version_.transport_version >= quic::QUIC_VERSION_43) ||
+      VersionHasStreamType(version_.transport_version)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
@@ -7084,8 +7087,10 @@
           1, GetNthClientInitiatedBidirectionalStreamId(0),
           GetNthServerInitiatedUnidirectionalStreamId(0), false,
           GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_));
-  if (client_headers_include_h2_stream_dependency_ &&
-      version_.transport_version >= quic::QUIC_VERSION_43) {
+
+  if ((client_headers_include_h2_stream_dependency_ &&
+       version_.transport_version >= quic::QUIC_VERSION_43) ||
+      VersionHasStreamType(version_.transport_version)) {
     mock_quic_data.AddWrite(
         SYNCHRONOUS,
         ConstructClientPriorityPacket(
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 1e47b7fe..c2a246f 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -1383,13 +1383,35 @@
   }
   int weight = spdy::Spdy3PriorityToHttp2Weight(priority);
   bool exclusive = client_headers_include_h2_stream_dependency_;
-  spdy::SpdyPriorityIR priority_frame(id, parent_stream_id, weight, exclusive);
-  spdy::SpdySerializedFrame spdy_frame(
-      spdy_request_framer_.SerializeFrame(priority_frame));
+  if (!VersionUsesQpack(version_.transport_version)) {
+    spdy::SpdyPriorityIR priority_frame(id, parent_stream_id, weight,
+                                        exclusive);
+    spdy::SpdySerializedFrame spdy_frame(
+        spdy_request_framer_.SerializeFrame(priority_frame));
 
-  quic::QuicStreamFrame quic_frame = GenerateNextStreamFrame(
-      quic::QuicUtils::GetHeadersStreamId(version_.transport_version), false,
-      quic::QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
+    quic::QuicStreamFrame quic_frame = GenerateNextStreamFrame(
+        quic::QuicUtils::GetHeadersStreamId(version_.transport_version), false,
+        quic::QuicStringPiece(spdy_frame.data(), spdy_frame.size()));
+    InitializeHeader(packet_number, should_include_version);
+    return MakePacket(header_, quic::QuicFrame(quic_frame));
+  }
+  quic::PriorityFrame frame;
+  frame.weight = weight;
+  frame.exclusive = true;
+  frame.prioritized_element_id = id;
+  frame.element_dependency_id = parent_stream_id;
+  frame.dependency_type = quic::REQUEST_STREAM;
+  frame.prioritized_type =
+      quic::QuicUtils::IsServerInitiatedStreamId(version_.transport_version, id)
+          ? quic::PUSH_STREAM
+          : quic::REQUEST_STREAM;
+  std::unique_ptr<char[]> buffer;
+  quic::QuicByteCount frame_length =
+      http_encoder_.SerializePriorityFrame(frame, &buffer);
+  std::string priority_data = std::string(buffer.get(), frame_length);
+
+  quic::QuicStreamFrame quic_frame =
+      GenerateNextStreamFrame(2, false, priority_data);
   InitializeHeader(packet_number, should_include_version);
   return MakePacket(header_, quic::QuicFrame(quic_frame));
 }
diff --git a/net/spdy/platform/impl/spdy_containers_impl.h b/net/spdy/platform/impl/spdy_containers_impl.h
index 5792eb7..1a5381a 100644
--- a/net/spdy/platform/impl/spdy_containers_impl.h
+++ b/net/spdy/platform/impl/spdy_containers_impl.h
@@ -8,6 +8,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "base/containers/small_map.h"
 #include "base/strings/string_piece.h"
 #include "net/third_party/quiche/src/common/simple_linked_hash_map.h"
 
@@ -34,6 +35,12 @@
   return base::StringPieceHash()(a) ^ base::StringPieceHash()(b);
 }
 
+// A map which is faster than (for example) hash_map for a certain number of
+// unique key-value-pair elements, and upgrades itself to unordered_map when
+// runs out of space.
+template <typename Key, typename Value, size_t Size>
+using SpdySmallMapImpl = base::small_map<std::unordered_map<Key, Value>, Size>;
+
 }  // namespace spdy
 
 #endif  // NET_SPDY_PLATFORM_IMPL_SPDY_CONTAINERS_IMPL_H_
diff --git a/pdf/draw_utils/coordinates.cc b/pdf/draw_utils/coordinates.cc
index f18bb56..569f79ed 100644
--- a/pdf/draw_utils/coordinates.cc
+++ b/pdf/draw_utils/coordinates.cc
@@ -80,13 +80,14 @@
   return pp::Rect(x, y, right - x, bottom - y);
 }
 
-pp::Rect GetSurroundingRect(const pp::Rect& rect,
+pp::Rect GetSurroundingRect(int page_y,
+                            int page_height,
                             const PageInsetSizes& inset_sizes,
                             int doc_width,
                             int bottom_separator) {
   return pp::Rect(
-      0, rect.y() - inset_sizes.top, doc_width,
-      rect.height() + inset_sizes.top + inset_sizes.bottom + bottom_separator);
+      0, page_y - inset_sizes.top, doc_width,
+      page_height + inset_sizes.top + inset_sizes.bottom + bottom_separator);
 }
 
 pp::Rect GetLeftRectForTwoUpView(const pp::Size& rect_size,
diff --git a/pdf/draw_utils/coordinates.h b/pdf/draw_utils/coordinates.h
index 265c6eb..602d6b9c 100644
--- a/pdf/draw_utils/coordinates.h
+++ b/pdf/draw_utils/coordinates.h
@@ -73,11 +73,12 @@
                        const pp::Point& position,
                        double zoom);
 
-// Given |rect|, |inset_sizes|, |doc_width|, and |bottom_separator|  all in the
-// same coordinate space, return |rect| and its surrounding border areas and
-// |bottom_separator|. This includes the sides if the page is narrower than the
-// document.
-pp::Rect GetSurroundingRect(const pp::Rect& rect,
+// Given |page_y|, |page_height|, |inset_sizes|, |doc_width|, and
+// |bottom_separator| all in the same coordinate space, return the page and its
+// surrounding border areas and |bottom_separator|. This includes the sides if
+// the page is narrower than the document.
+pp::Rect GetSurroundingRect(int page_y,
+                            int page_height,
                             const PageInsetSizes& inset_sizes,
                             int doc_width,
                             int bottom_separator);
diff --git a/pdf/draw_utils/coordinates_unittest.cc b/pdf/draw_utils/coordinates_unittest.cc
index ab4d4d52..5e8a6a0a 100644
--- a/pdf/draw_utils/coordinates_unittest.cc
+++ b/pdf/draw_utils/coordinates_unittest.cc
@@ -263,32 +263,29 @@
   constexpr int kDocWidth = 1000;
 
   // Test various position, sizes, and document width.
-  pp::Rect rect(0, 100, 200, 300);
-  rect =
-      GetSurroundingRect(rect, kSingleViewInsets, kDocWidth, kBottomSeparator);
+  pp::Rect rect = GetSurroundingRect(100, 300, kSingleViewInsets, kDocWidth,
+                                     kBottomSeparator);
   EXPECT_EQ(0, rect.x());
   EXPECT_EQ(97, rect.y());
   EXPECT_EQ(1000, rect.width());
   EXPECT_EQ(314, rect.height());
 
-  rect.SetRect(1000, 40, 5000, 200);
-  rect =
-      GetSurroundingRect(rect, kSingleViewInsets, kDocWidth, kBottomSeparator);
+  rect = GetSurroundingRect(40, 200, kSingleViewInsets, kDocWidth,
+                            kBottomSeparator);
   EXPECT_EQ(0, rect.x());
   EXPECT_EQ(37, rect.y());
   EXPECT_EQ(1000, rect.width());
   EXPECT_EQ(214, rect.height());
 
-  rect.SetRect(-100, 200, 300, 500);
-  rect =
-      GetSurroundingRect(rect, kSingleViewInsets, kDocWidth, kBottomSeparator);
+  rect = GetSurroundingRect(200, 500, kSingleViewInsets, kDocWidth,
+                            kBottomSeparator);
   EXPECT_EQ(0, rect.x());
   EXPECT_EQ(197, rect.y());
   EXPECT_EQ(1000, rect.width());
   EXPECT_EQ(514, rect.height());
 
-  rect.SetRect(10, -100, 4000, 300);
-  rect = GetSurroundingRect(rect, kSingleViewInsets, 200, kBottomSeparator);
+  rect =
+      GetSurroundingRect(-100, 300, kSingleViewInsets, 200, kBottomSeparator);
   EXPECT_EQ(0, rect.x());
   EXPECT_EQ(-103, rect.y());
   EXPECT_EQ(200, rect.width());
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index b636d6b..d0b0d74 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -2833,17 +2833,24 @@
   return size;
 }
 
-void PDFiumEngine::EnlargePage(size_t page_index,
-                               size_t num_of_pages,
-                               pp::Size* page_size) {
+draw_utils::PageInsetSizes PDFiumEngine::GetInsetSizes(
+    size_t page_index,
+    size_t num_of_pages) const {
   DCHECK_LT(page_index, num_of_pages);
 
-  draw_utils::PageInsetSizes inset_sizes = kSingleViewInsets;
   if (two_up_view_) {
-    inset_sizes = draw_utils::GetPageInsetsForTwoUpView(
+    return draw_utils::GetPageInsetsForTwoUpView(
         page_index, num_of_pages, kSingleViewInsets, kHorizontalSeparator);
   }
 
+  return kSingleViewInsets;
+}
+
+void PDFiumEngine::EnlargePage(size_t page_index,
+                               size_t num_of_pages,
+                               pp::Size* page_size) const {
+  draw_utils::PageInsetSizes inset_sizes =
+      GetInsetSizes(page_index, num_of_pages);
   page_size->Enlarge(inset_sizes.left + inset_sizes.right,
                      inset_sizes.top + inset_sizes.bottom);
 }
@@ -2851,15 +2858,9 @@
 void PDFiumEngine::InsetPage(size_t page_index,
                              size_t num_of_pages,
                              double multiplier,
-                             pp::Rect* rect) {
-  DCHECK_LT(page_index, num_of_pages);
-
-  draw_utils::PageInsetSizes inset_sizes = kSingleViewInsets;
-  if (two_up_view_) {
-    inset_sizes = draw_utils::GetPageInsetsForTwoUpView(
-        page_index, num_of_pages, kSingleViewInsets, kHorizontalSeparator);
-  }
-
+                             pp::Rect* rect) const {
+  draw_utils::PageInsetSizes inset_sizes =
+      GetInsetSizes(page_index, num_of_pages);
   rect->Inset(static_cast<int>(ceil(inset_sizes.left * multiplier)),
               static_cast<int>(ceil(inset_sizes.top * multiplier)),
               static_cast<int>(ceil(inset_sizes.right * multiplier)),
@@ -3139,7 +3140,8 @@
 pp::Rect PDFiumEngine::GetPageScreenRect(int page_index) const {
   const pp::Rect& page_rect = pages_[page_index]->rect();
   return GetScreenRect(draw_utils::GetSurroundingRect(
-      page_rect, kSingleViewInsets, document_size_.width(), kBottomSeparator));
+      page_rect.y(), page_rect.height(), kSingleViewInsets,
+      document_size_.width(), kBottomSeparator));
 }
 
 pp::Rect PDFiumEngine::GetScreenRect(const pp::Rect& rect) const {
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index e1b7ac9..5aa32c2 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -41,6 +41,7 @@
 
 namespace draw_utils {
 class ShadowMatrix;
+struct PageInsetSizes;
 }
 
 class PDFiumEngine : public PDFEngine,
@@ -267,18 +268,26 @@
   // PDFiumPage because we might not have that structure when we need this.
   pp::Size GetPageSize(int index);
 
+  // Helper function for getting the inset sizes for the current layout. If
+  // |two_up_view_| is true, the configuration of inset sizes depends on
+  // the position of the page, specified by |page_index| and |num_of_pages|.
+  draw_utils::PageInsetSizes GetInsetSizes(size_t page_index,
+                                           size_t num_of_pages) const;
+
   // If |two_up_view_| is false, enlarges |page_size| with inset sizes for
-  // single-view. If |two_up_view_| is true, gets the appropriate two-up view
-  // inset sizes for the position of the page (dependent on |page_index| and
-  // |num_of_pages|) and then enlarges |page_size|.
-  void EnlargePage(size_t page_index, size_t num_of_pages, pp::Size* page_size);
+  // single-view. If |two_up_view_| is true, calls GetInsetSizes() with
+  // |page_index| and |num_of_pages|, and uses the returned inset sizes to
+  // enlarge |page_size|.
+  void EnlargePage(size_t page_index,
+                   size_t num_of_pages,
+                   pp::Size* page_size) const;
 
   // Similar to EnlargePage(), but insets a |rect|. Also multiplies the inset
   // sizes by |multiplier|, using the ceiling of the result.
   void InsetPage(size_t page_index,
                  size_t num_of_pages,
                  double multiplier,
-                 pp::Rect* rect);
+                 pp::Rect* rect) const;
 
   void GetAllScreenRectsUnion(const std::vector<PDFiumRange>& rect_range,
                               const pp::Point& offset_point,
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 3cf5a41..c80508bb 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -31,7 +31,6 @@
 #include "net/cert/cert_database.h"
 #include "net/cert/ct_log_response_parser.h"
 #include "net/cert/signed_tree_head.h"
-#include "net/dns/dns_config.h"
 #include "net/dns/dns_config_overrides.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_manager.h"
@@ -451,17 +450,16 @@
 }
 
 void NetworkService::ConfigureStubHostResolver(
-    bool stub_resolver_enabled,
+    bool insecure_dns_client_enabled,
+    net::DnsConfig::SecureDnsMode secure_dns_mode,
     base::Optional<std::vector<mojom::DnsOverHttpsServerPtr>>
         dns_over_https_servers) {
-  // If the stub resolver is not enabled, |dns_over_https_servers| has no
-  // effect.
-  DCHECK(stub_resolver_enabled || !dns_over_https_servers);
   DCHECK(!dns_over_https_servers || !dns_over_https_servers->empty());
 
   // Enable or disable the insecure part of DnsClient. "DnsClient" is the class
   // that implements the stub resolver.
-  host_resolver_manager_->SetInsecureDnsClientEnabled(stub_resolver_enabled);
+  host_resolver_manager_->SetInsecureDnsClientEnabled(
+      insecure_dns_client_enabled);
 
   // Configure DNS over HTTPS.
   if (!dns_over_https_servers || dns_over_https_servers.value().empty()) {
@@ -475,9 +473,7 @@
     overrides.dns_over_https_servers.value().emplace_back(
         doh_server->server_template, doh_server->use_post);
   }
-  // TODO(crbug.com/985589): Allow the secure dns mode to be set independently
-  // of the insecure part of the stub resolver.
-  overrides.secure_dns_mode = net::DnsConfig::SecureDnsMode::AUTOMATIC;
+  overrides.secure_dns_mode = secure_dns_mode;
   host_resolver_manager_->SetDnsConfigOverrides(overrides);
 }
 
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 5ce34457..633cebdd 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -23,6 +23,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "net/dns/dns_config.h"
 #include "net/http/http_auth_preferences.h"
 #include "net/log/net_log.h"
 #include "net/log/trace_net_log_observer.h"
@@ -142,7 +143,8 @@
   void CreateNetworkContext(mojom::NetworkContextRequest request,
                             mojom::NetworkContextParamsPtr params) override;
   void ConfigureStubHostResolver(
-      bool stub_resolver_enabled,
+      bool insecure_dns_client_enabled,
+      net::DnsConfig::SecureDnsMode secure_dns_mode,
       base::Optional<std::vector<mojom::DnsOverHttpsServerPtr>>
           dns_over_https_servers) override;
   void DisableQuic() override;
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index 7f5af33..2f1ac99 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "net/base/escape.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/url_util.h"
 #include "net/dns/dns_config_service.h"
@@ -443,23 +444,55 @@
 #if !defined(OS_IOS)
 
 TEST_F(NetworkServiceTest, DnsClientEnableDisable) {
-  // HostResolver::GetDnsConfigAsValue() returns nullptr if the stub resolver is
-  // disabled.
-  EXPECT_FALSE(service()
-                   ->host_resolver_manager()
-                   ->GetInsecureDnsClientEnabledForTesting());
+  // Set valid DnsConfig.
+  net::DnsConfig config;
+  config.nameservers.push_back(net::IPEndPoint());
+  service()->host_resolver_manager()->SetBaseDnsConfigForTesting(config);
+
   service()->ConfigureStubHostResolver(
-      true /* stub_resolver_enabled */,
+      true /* insecure_dns_client_enabled */,
+      net::DnsConfig::SecureDnsMode::OFF,
       base::nullopt /* dns_over_https_servers */);
   EXPECT_TRUE(service()
                   ->host_resolver_manager()
                   ->GetInsecureDnsClientEnabledForTesting());
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF,
+            service()->host_resolver_manager()->GetSecureDnsModeForTesting());
+
   service()->ConfigureStubHostResolver(
-      false /* stub_resolver_enabled */,
+      false /* insecure_dns_client_enabled */,
+      net::DnsConfig::SecureDnsMode::OFF,
       base::nullopt /* dns_over_https_servers */);
   EXPECT_FALSE(service()
                    ->host_resolver_manager()
                    ->GetInsecureDnsClientEnabledForTesting());
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF,
+            service()->host_resolver_manager()->GetSecureDnsModeForTesting());
+
+  service()->ConfigureStubHostResolver(
+      false /* insecure_dns_client_enabled */,
+      net::DnsConfig::SecureDnsMode::AUTOMATIC,
+      base::nullopt /* dns_over_https_servers */);
+  EXPECT_FALSE(service()
+                   ->host_resolver_manager()
+                   ->GetInsecureDnsClientEnabledForTesting());
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF,
+            service()->host_resolver_manager()->GetSecureDnsModeForTesting());
+
+  std::vector<mojom::DnsOverHttpsServerPtr> dns_over_https_servers_ptr;
+  mojom::DnsOverHttpsServerPtr dns_over_https_server =
+      mojom::DnsOverHttpsServer::New();
+  dns_over_https_server->server_template = "https://foo/";
+  dns_over_https_server->use_post = true;
+  dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
+  service()->ConfigureStubHostResolver(false /* insecure_dns_client_enabled */,
+                                       net::DnsConfig::SecureDnsMode::AUTOMATIC,
+                                       std::move(dns_over_https_servers_ptr));
+  EXPECT_FALSE(service()
+                   ->host_resolver_manager()
+                   ->GetInsecureDnsClientEnabledForTesting());
+  EXPECT_EQ(net::DnsConfig::SecureDnsMode::AUTOMATIC,
+            service()->host_resolver_manager()->GetSecureDnsModeForTesting());
 }
 
 TEST_F(NetworkServiceTest, DnsOverHttpsEnableDisable) {
@@ -487,7 +520,8 @@
   dns_over_https_server->use_post = kServer1UsePost;
   dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
 
-  service()->ConfigureStubHostResolver(true /* stub_resolver_enabled */,
+  service()->ConfigureStubHostResolver(false /* insecure_dns_client_enabled */,
+                                       net::DnsConfig::SecureDnsMode::AUTOMATIC,
                                        std::move(dns_over_https_servers_ptr));
   EXPECT_TRUE(service()->host_resolver_manager()->GetDnsConfigAsValue());
   const auto* dns_over_https_servers =
@@ -510,7 +544,8 @@
   dns_over_https_server->use_post = kServer3UsePost;
   dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
 
-  service()->ConfigureStubHostResolver(true /* stub_resolver_enabled */,
+  service()->ConfigureStubHostResolver(true /* insecure_dns_client_enabled */,
+                                       net::DnsConfig::SecureDnsMode::SECURE,
                                        std::move(dns_over_https_servers_ptr));
   EXPECT_TRUE(service()->host_resolver_manager()->GetDnsConfigAsValue());
   dns_over_https_servers =
diff --git a/services/network/public/cpp/host_resolver.typemap b/services/network/public/cpp/host_resolver.typemap
index 20cb2428..77691a20 100644
--- a/services/network/public/cpp/host_resolver.typemap
+++ b/services/network/public/cpp/host_resolver.typemap
@@ -1,9 +1,10 @@
 # Copyright 2018 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.

+# found in the LICENSE file.
 
 mojom = "//services/network/public/mojom/host_resolver.mojom"
 public_headers = [
+  "//net/dns/dns_config.h",
   "//net/dns/dns_config_overrides.h",
   "//net/dns/host_resolver.h",
   "//net/dns/host_resolver_source.h",
@@ -22,4 +23,5 @@
   "network.mojom.DnsQueryType=net::DnsQueryType",
   "network.mojom.ResolveHostParameters.Source=net::HostResolverSource",
   "network.mojom.MdnsListenClient.UpdateType=net::HostResolver::MdnsListener::Delegate::UpdateType",
+  "network.mojom.SecureDnsMode=net::DnsConfig::SecureDnsMode",
 ]
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index 0192278..3796681 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -373,4 +373,38 @@
   }
 }
 
+// static
+network::mojom::SecureDnsMode
+EnumTraits<network::mojom::SecureDnsMode, net::DnsConfig::SecureDnsMode>::
+    ToMojom(net::DnsConfig::SecureDnsMode secure_dns_mode) {
+  switch (secure_dns_mode) {
+    case net::DnsConfig::SecureDnsMode::OFF:
+      return network::mojom::SecureDnsMode::OFF;
+    case net::DnsConfig::SecureDnsMode::AUTOMATIC:
+      return network::mojom::SecureDnsMode::AUTOMATIC;
+    case net::DnsConfig::SecureDnsMode::SECURE:
+      return network::mojom::SecureDnsMode::SECURE;
+  }
+  NOTREACHED();
+  return network::mojom::SecureDnsMode::OFF;
+}
+
+// static
+bool EnumTraits<network::mojom::SecureDnsMode, net::DnsConfig::SecureDnsMode>::
+    FromMojom(network::mojom::SecureDnsMode in,
+              net::DnsConfig::SecureDnsMode* out) {
+  switch (in) {
+    case network::mojom::SecureDnsMode::OFF:
+      *out = net::DnsConfig::SecureDnsMode::OFF;
+      return true;
+    case network::mojom::SecureDnsMode::AUTOMATIC:
+      *out = net::DnsConfig::SecureDnsMode::AUTOMATIC;
+      return true;
+    case network::mojom::SecureDnsMode::SECURE:
+      *out = net::DnsConfig::SecureDnsMode::SECURE;
+      return true;
+  }
+  return false;
+}
+
 }  // namespace mojo
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.h b/services/network/public/cpp/host_resolver_mojom_traits.h
index b6c7ac44..b7e175b 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.h
+++ b/services/network/public/cpp/host_resolver_mojom_traits.h
@@ -17,6 +17,7 @@
 #include "net/base/address_family.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
+#include "net/dns/dns_config.h"
 #include "net/dns/dns_config_overrides.h"
 #include "net/dns/dns_hosts.h"
 #include "net/dns/host_resolver.h"
@@ -105,6 +106,15 @@
       net::HostResolver::MdnsListener::Delegate::UpdateType* output);
 };
 
+template <>
+struct EnumTraits<network::mojom::SecureDnsMode,
+                  net::DnsConfig::SecureDnsMode> {
+  static network::mojom::SecureDnsMode ToMojom(
+      net::DnsConfig::SecureDnsMode secure_dns_mode);
+  static bool FromMojom(network::mojom::SecureDnsMode in,
+                        net::DnsConfig::SecureDnsMode* out);
+};
+
 }  // namespace mojo
 
 #endif  // SERVICES_NETWORK_PUBLIC_CPP_HOST_RESOLVER_MOJOM_TRAITS_H_
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index cd137d8e..fce8cc7 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -40,6 +40,13 @@
   SECURE,
 };
 
+// This enum corresponds to DnsConfig::SecureDnsMode.
+enum SecureDnsMode {
+  OFF,
+  AUTOMATIC,
+  SECURE,
+};
+
 // Overridable DNS configuration values for host resolution. All fields default
 // to a non-overriding state where the relevant value will be used from system
 // DNS configuration.
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index c51da43..7a426ab 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -324,19 +324,21 @@
   // resolver.
   //
   // |dns_over_https_servers| is an optional list of DNS over HTTPS servers.
-  // When populated, all DNS lookups will try to use DNS over HTTPS in the order
-  // the servers are provided in and will only fall back to using system
-  // settings if DNS over HTTPS fails. It is illegal to have a populated
-  // |dns_over_https_servers| when |stub_resolver_enabled| is false.
+  // DnsTransactions will by default follow the behavior of |secure_dns_mode|.
+  // In SECURE mode, only DoH lookups will be performed. In AUTOMATIC mode,
+  // DoH lookups to available servers will be performed first, and insecure
+  // lookups will be used as a fallback. In OFF mode, only insecure lookups will
+  // be performed. When insecure lookups are performed, they will be sent by
+  // the async resolver first if |insecure_dns_client_enabled| is true and
+  // then by the system resolver as a fallback.
   //
   // DNS over HTTPS will use the primary NetworkContext, so can only be enabled
   // after the primary network context has been created. Other than that
   // limitation, this method can be called at any time to change DNS
   // configuration, though calling it will fail any DNS lookups that have
   // already been started.
-  //
-  // Both the stub resolver and DNS over HTTPS are disabled by default.
-  ConfigureStubHostResolver(bool stub_resolver_enabled,
+  ConfigureStubHostResolver(bool insecure_dns_client_enabled,
+                            SecureDnsMode secure_dns_mode,
                             array<DnsOverHttpsServer>? dns_over_https_servers);
 
   // Disables QUIC for the NetworkService. Affects all existing NetworkContexts,
diff --git a/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc b/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc
index 35b86fe..4a02f15 100644
--- a/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc
+++ b/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc
@@ -16,6 +16,9 @@
 #include <fuchsia/netstack/cpp/fidl.h>
 #include <fuchsia/sysmem/cpp/fidl.h>
 #include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+#include <lib/sys/cpp/service_directory.h>
+
 #include <memory>
 #include <utility>
 
@@ -23,8 +26,8 @@
 #include "base/command_line.h"
 #include "base/containers/span.h"
 #include "base/files/file_util.h"
+#include "base/fuchsia/default_context.h"
 #include "base/fuchsia/filtered_service_directory.h"
-#include "base/fuchsia/service_directory_client.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/process/process.h"
@@ -144,7 +147,7 @@
     service_directory_task_runner_ = base::ThreadTaskRunnerHandle::Get();
     service_directory_ =
         std::make_unique<base::fuchsia::FilteredServiceDirectory>(
-            base::fuchsia::ServiceDirectoryClient::ForCurrentProcess());
+            base::fuchsia::ComponentContextForCurrentProcess()->svc().get());
     for (const char* service_name : kDefaultServices) {
       service_directory_->AddService(service_name);
     }
@@ -153,7 +156,7 @@
     }
     // Bind the service directory and store the client channel for
     // UpdateLaunchOptionsForSandbox()'s use.
-    service_directory_client_ = service_directory_->ConnectClient();
+    service_directory_->ConnectClient(service_directory_client_.NewRequest());
     CHECK(service_directory_client_);
   }
 }
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
index 0ffbd24d..5c3b4e1 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
@@ -7,6 +7,7 @@
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
 #include "services/tracing/public/cpp/perfetto/dummy_producer.h"
@@ -139,8 +140,9 @@
 }
 
 // static
-void PerfettoTracedProcess::ResetTaskRunnerForTesting() {
-  GetTaskRunner()->ResetTaskRunnerForTesting(nullptr);
+void PerfettoTracedProcess::ResetTaskRunnerForTesting(
+    scoped_refptr<base::SequencedTaskRunner> task_runner) {
+  GetTaskRunner()->ResetTaskRunnerForTesting(task_runner);
   // Detaching the sequence_checker_ must happen after we reset the task runner.
   // This is because the Get() could call the constructor (if this is the first
   // call to Get()) which would then PostTask which would create races if we
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
index 8f864e7..1738cef 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
@@ -7,6 +7,7 @@
 
 #include "base/component_export.h"
 #include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
 #include "services/tracing/public/cpp/perfetto/task_runner.h"
 
 namespace tracing {
@@ -102,7 +103,8 @@
   // Be careful when using ResetTaskRunnerForTesting. There is a PostTask in the
   // constructor of PerfettoTracedProcess, so before this class is constructed
   // is the only safe time to call this.
-  static void ResetTaskRunnerForTesting();
+  static void ResetTaskRunnerForTesting(
+      scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr);
 
   static void ReconstructForTesting(const char* system_socket);
 
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index 851c5f3..8afe51f 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -6,6 +6,7 @@
 
 #include <atomic>
 #include <map>
+#include <memory>
 #include <utility>
 
 #include "base/base64.h"
@@ -19,8 +20,12 @@
 #include "base/metrics/statistics_recorder.h"
 #include "base/no_destructor.h"
 #include "base/pickle.h"
+#include "base/sequence_checker.h"
 #include "base/task/common/scoped_defer_task_posting.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_log.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_producer.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
@@ -235,6 +240,7 @@
       disable_interning_(base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kPerfettoDisableInterning)) {
   g_trace_event_data_source_for_testing = this;
+  DETACH_FROM_SEQUENCE(perfetto_sequence_checker_);
 }
 
 TraceEventDataSource::~TraceEventDataSource() = default;
@@ -250,11 +256,19 @@
 void TraceEventDataSource::UnregisterFromTraceLog() {
   RegisterTracedValueProtoWriter(false);
   TraceLog::GetInstance()->SetAddTraceEventOverrides(nullptr, nullptr, nullptr);
+  base::AutoLock l(lock_);
+  flushing_trace_log_ = false;
+  DCHECK(!flush_complete_task_);
 }
 
 void TraceEventDataSource::SetupStartupTracing(bool privacy_filtering_enabled) {
   {
     base::AutoLock lock(lock_);
+    // Do not enable startup registry if trace log is being flushed. The
+    // previous tracing session has not ended yet.
+    if (flushing_trace_log_) {
+      return;
+    }
     // No need to do anything if startup tracing has already been set,
     // or we know Perfetto has already been setup.
     if (startup_writer_registry_ || producer_) {
@@ -267,11 +281,97 @@
         std::make_unique<perfetto::StartupTraceWriterRegistry>();
   }
   RegisterWithTraceLog();
+  if (base::SequencedTaskRunnerHandle::IsSet()) {
+    OnTaskSchedulerAvailable();
+  }
+}
+
+void TraceEventDataSource::OnTaskSchedulerAvailable() {
+  {
+    base::AutoLock lock(lock_);
+    if (!startup_writer_registry_)
+      return;
+  }
+  startup_tracing_timer_.Start(
+      FROM_HERE, startup_tracing_timeout_,
+      base::BindOnce(&TraceEventDataSource::StartupTracingTimeoutFired,
+                     base::Unretained(this)));
+}
+
+void TraceEventDataSource::StartupTracingTimeoutFired() {
+  auto task_runner =
+      PerfettoTracedProcess::Get()->GetTaskRunner()->GetOrCreateTaskRunner();
+  if (!task_runner->RunsTasksInCurrentSequence()) {
+    task_runner->PostTask(
+        FROM_HERE,
+        base::BindOnce(&TraceEventDataSource::StartupTracingTimeoutFired,
+                       base::Unretained(this)));
+    return;
+  }
+  DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
+  std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry;
+  {
+    base::AutoLock lock(lock_);
+    if (!startup_writer_registry_) {
+      return;
+    }
+    // Set startup_writer_registry_ to null so that no further writers are
+    // created.
+    startup_writer_registry_.reset();
+    flushing_trace_log_ = true;
+  }
+  auto* trace_log = base::trace_event::TraceLog::GetInstance();
+  trace_log->SetDisabled();
+  trace_log->Flush(base::BindRepeating(&TraceEventDataSource::OnFlushFinished,
+                                       base::Unretained(this)),
+                   /*use_worker_thread=*/false);
+}
+
+void TraceEventDataSource::OnFlushFinished(
+    const scoped_refptr<base::RefCountedString>&,
+    bool has_more_events) {
+  if (has_more_events) {
+    return;
+  }
+
+  // Clear the pending task on the tracing service thread.
+  DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
+  base::OnceClosure task;
+  {
+    base::AutoLock l(lock_);
+    // Run any pending start or stop tracing
+    // task.
+    task = std::move(flush_complete_task_);
+    flushing_trace_log_ = false;
+  }
+  if (task) {
+    std::move(task).Run();
+  }
 }
 
 void TraceEventDataSource::StartTracing(
     PerfettoProducer* producer,
     const perfetto::DataSourceConfig& data_source_config) {
+  {
+    base::AutoLock l(lock_);
+    if (flushing_trace_log_) {
+      DCHECK(!flush_complete_task_);
+      // Delay start tracing until flush is finished.
+      // Unretained is fine here because the producer will be valid till
+      // stop tracing is called and at stop this task will be cleared.
+      flush_complete_task_ = base::BindOnce(
+          &TraceEventDataSource::StartTracingInternal, base::Unretained(this),
+          base::Unretained(producer), data_source_config);
+      return;
+    }
+  }
+  StartTracingInternal(producer, data_source_config);
+}
+
+void TraceEventDataSource::StartTracingInternal(
+    PerfettoProducer* producer,
+    const perfetto::DataSourceConfig& data_source_config) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
   std::unique_ptr<perfetto::StartupTraceWriterRegistry> unbound_writer_registry;
   {
     base::AutoLock lock(lock_);
@@ -312,7 +412,7 @@
 
 void TraceEventDataSource::StopTracing(
     base::OnceClosure stop_complete_callback) {
-  LogHistograms();
+  DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
   stop_complete_callback_ = std::move(stop_complete_callback);
 
   auto on_tracing_stopped_callback =
@@ -332,15 +432,28 @@
   bool was_enabled = TraceLog::GetInstance()->IsEnabled();
   if (was_enabled) {
     // Write metadata events etc.
+    LogHistograms();
     TraceLog::GetInstance()->SetDisabled();
   }
 
   {
-    // Prevent recreation of ThreadLocalEventSinks after flush.
     base::AutoLock lock(lock_);
+    if (flush_complete_task_) {
+      DCHECK(!producer_);
+      // Skip start tracing task at this point if we still have not flushed
+      // trace log. We wouldn't be replacing a |flush_complete_task_| that is
+      // stop tracing callback task at any point, since perfetto will wait for
+      // the callback before starting next session.
+      flush_complete_task_ =
+          base::BindOnce(std::move(on_tracing_stopped_callback), this,
+                         scoped_refptr<base::RefCountedString>(), false);
+      return;
+    }
+    // Prevent recreation of ThreadLocalEventSinks after flush.
     DCHECK(producer_);
     producer_ = nullptr;
     target_buffer_ = 0;
+    flushing_trace_log_ = was_enabled;
   }
 
   if (was_enabled) {
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
index 1bf63d0..0072001c 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -13,8 +13,10 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_base.h"
+#include "base/sequence_checker.h"
 #include "base/threading/thread_local.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "base/trace_event/trace_config.h"
 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
@@ -114,6 +116,8 @@
   // service. Should only be called once.
   void SetupStartupTracing(bool privacy_filtering_enabled);
 
+  void OnTaskSchedulerAvailable();
+
   // The PerfettoProducer is responsible for calling StopTracing
   // which will clear the stored pointer to it, before it
   // gets destroyed. PerfettoProducer::CreateTraceWriter can be
@@ -131,12 +135,24 @@
   void ReturnTraceWriter(
       std::unique_ptr<perfetto::StartupTraceWriter> trace_writer);
 
+  void set_startup_tracing_timeout_for_testing(base::TimeDelta timeout_us) {
+    startup_tracing_timeout_ = timeout_us;
+  }
+
  private:
   friend class base::NoDestructor<TraceEventDataSource>;
 
   TraceEventDataSource();
   ~TraceEventDataSource() override;
 
+  void StartupTracingTimeoutFired();
+  void OnFlushFinished(const scoped_refptr<base::RefCountedString>&,
+                       bool has_more_events);
+
+  void StartTracingInternal(
+      PerfettoProducer* producer_client,
+      const perfetto::DataSourceConfig& data_source_config);
+
   void RegisterWithTraceLog();
   void UnregisterFromTraceLog();
 
@@ -167,6 +183,7 @@
   // This ID is incremented whenever a new tracing session is started.
   static constexpr uint32_t kInvalidSessionID = 0;
   static constexpr uint32_t kFirstSessionID = 1;
+  base::TimeDelta startup_tracing_timeout_ = base::TimeDelta::FromSeconds(10);
   std::atomic<uint32_t> session_id_{kInvalidSessionID};
 
   // To avoid lock-order inversion, this lock should not be held while making
@@ -180,8 +197,12 @@
   // SetupStartupTracing() is called.
   std::unique_ptr<perfetto::StartupTraceWriterRegistry>
       startup_writer_registry_;
+  base::OneShotTimer startup_tracing_timer_;
+  bool flushing_trace_log_ = false;
+  base::OnceClosure flush_complete_task_;
   std::vector<std::string> histograms_;
   bool privacy_filtering_enabled_ = false;
+  SEQUENCE_CHECKER(perfetto_sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(TraceEventDataSource);
 };
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index 7d0050c..a470a01a 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -5,6 +5,7 @@
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
 
 #include <map>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -15,10 +16,16 @@
 #include "base/json/json_reader.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
 #include "base/trace_event/thread_instruction_count.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_log.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "services/tracing/public/cpp/perfetto/producer_client.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
@@ -118,6 +125,11 @@
     return &proto_metadata_packets_[packet_index]->chrome_metadata();
   }
 
+  const std::vector<std::unique_ptr<perfetto::protos::TracePacket>>&
+  finalized_packets() {
+    return finalized_packets_;
+  }
+
  private:
   std::vector<std::unique_ptr<perfetto::protos::TracePacket>>
       finalized_packets_;
@@ -1139,6 +1151,80 @@
   ExpectDebugAnnotationNames(e_packet3, {{1u, "arg2"}});
 }
 
+TEST_F(TraceEventDataSourceTest, StartupTracingTimeout) {
+  PerfettoTracedProcess::ResetTaskRunnerForTesting(
+      base::SequencedTaskRunnerHandle::Get());
+  constexpr char kStartupTestEvent1[] = "startup_registry";
+  TraceEventDataSource::ResetForTesting();
+  auto* data_source = TraceEventDataSource::GetInstance();
+
+  // Start startup tracing registry with no timeout. This would cause startup
+  // tracing to abort and flush as soon the current thread can run tasks.
+  data_source->set_startup_tracing_timeout_for_testing(base::TimeDelta());
+  data_source->SetupStartupTracing(true);
+  base::trace_event::TraceLog::GetInstance()->SetEnabled(
+      base::trace_event::TraceConfig(),
+      base::trace_event::TraceLog::RECORDING_MODE);
+
+  // The trace event will be added to the startup registry since the abort is
+  // not run yet.
+  TRACE_EVENT_BEGIN0(kCategoryGroup, kStartupTestEvent1);
+
+  // Run task on background thread to add trace events while aborting and
+  // starting tracing on the data source. This is to test we do not have any
+  // crashes when a background thread is trying to create trace writers when
+  // deleting startup registry and setting the producer.
+  auto wait_for_start_tracing = std::make_unique<base::WaitableEvent>();
+  base::WaitableEvent* wait_ptr = wait_for_start_tracing.get();
+  base::PostTaskWithTraits(
+      FROM_HERE, base::TaskPriority::BEST_EFFORT,
+      base::BindOnce(
+          [](std::unique_ptr<base::WaitableEvent> wait_for_start_tracing) {
+            // This event can be hit anytime before startup registry is
+            // destroyed to tracing started using producer.
+            TRACE_EVENT_BEGIN0(kCategoryGroup, "maybe_lost");
+            base::ScopedAllowBaseSyncPrimitivesForTesting allow;
+            wait_for_start_tracing->Wait();
+            // This event can be hit while flushing for startup registry or when
+            // tracing is started or when already stopped tracing.
+            TRACE_EVENT_BEGIN0(kCategoryGroup, "maybe_lost");
+          },
+          std::move(wait_for_start_tracing)));
+
+  // Let tasks run on this thread, which should abort startup tracing and flush
+  // since we have not added a producer to the data source.
+  data_source->OnTaskSchedulerAvailable();
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(base::trace_event::TraceLog::GetInstance()->IsEnabled());
+
+  // Start tracing while flush is running.
+  perfetto::DataSourceConfig config;
+  data_source->StartTracing(producer_client(), config);
+  wait_ptr->Signal();
+
+  // Verify that the trace buffer does not have the event added to startup
+  // registry.
+  producer_client()->FlushPacketIfPossible();
+  std::set<std::string> event_names;
+  for (const auto& packet : producer_client()->finalized_packets()) {
+    if (packet->has_interned_data()) {
+      for (const auto& name : packet->interned_data().legacy_event_names()) {
+        event_names.insert(name.name());
+      }
+    }
+  }
+  EXPECT_EQ(event_names.end(), event_names.find(kStartupTestEvent1));
+
+  // Stop tracing must be called even if tracing is not started to clear the
+  // pending task.
+  base::RunLoop wait_for_stop;
+  data_source->StopTracing(base::BindRepeating(
+      [](const base::RepeatingClosure& quit_closure) { quit_closure.Run(); },
+      wait_for_stop.QuitClosure()));
+
+  wait_for_stop.Run();
+}
+
 // TODO(eseckler): Add startup tracing unittests.
 
 }  // namespace
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index cc7b9814..ed451c4 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -61,4 +61,8 @@
   }
 }
 
+void InitTracingPostThreadPoolStart() {
+  TraceEventDataSource::GetInstance()->OnTaskSchedulerAvailable();
+}
+
 }  // namespace tracing
diff --git a/services/tracing/public/cpp/trace_startup.h b/services/tracing/public/cpp/trace_startup.h
index a2dd3668..0273ef8f 100644
--- a/services/tracing/public/cpp/trace_startup.h
+++ b/services/tracing/public/cpp/trace_startup.h
@@ -15,6 +15,9 @@
 // tracing backend is used.
 void COMPONENT_EXPORT(TRACING_CPP) EnableStartupTracingIfNeeded();
 
+// Initialize tracing components that require task runners.
+void COMPONENT_EXPORT(TRACING_CPP) InitTracingPostThreadPoolStart();
+
 }  // namespace tracing
 
 #endif  // SERVICES_TRACING_PUBLIC_CPP_TRACE_STARTUP_H_
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index e8aa895..3a0ba30 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -2,30 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//services/service_manager/public/cpp/service_executable.gni")
 import("//testing/test.gni")
 
-service_executable("video_capture") {
-  sources = [
-    "service_main.cc",
-  ]
-
-  deps = [
-    ":lib",
-    "//mojo/public/cpp/system",
-    "//services/video_capture/public/cpp",
-    "//services/video_capture/public/mojom",
-  ]
-}
-
 source_set("lib") {
   sources = [
     "broadcasting_receiver.cc",
     "broadcasting_receiver.h",
     "device_factory_media_to_mojo_adapter.cc",
     "device_factory_media_to_mojo_adapter.h",
-    "device_factory_provider_impl.cc",
-    "device_factory_provider_impl.h",
     "device_media_to_mojo_adapter.cc",
     "device_media_to_mojo_adapter.h",
     "push_video_stream_subscription_impl.cc",
@@ -34,14 +18,14 @@
     "receiver_mojo_to_media_adapter.h",
     "scoped_access_permission_media_to_mojo_adapter.cc",
     "scoped_access_permission_media_to_mojo_adapter.h",
-    "service_impl.cc",
-    "service_impl.h",
     "shared_memory_virtual_device_mojo_adapter.cc",
     "shared_memory_virtual_device_mojo_adapter.h",
     "testing_controls_impl.cc",
     "testing_controls_impl.h",
     "texture_virtual_device_mojo_adapter.cc",
     "texture_virtual_device_mojo_adapter.h",
+    "video_capture_service_impl.cc",
+    "video_capture_service_impl.h",
     "video_source_impl.cc",
     "video_source_impl.h",
     "video_source_provider_impl.cc",
@@ -59,7 +43,6 @@
     "//media/capture/mojom:image_capture",
     "//media/mojo/common:common",
     "//mojo/public/cpp/system",
-    "//services/service_manager/public/cpp",
     "//services/video_capture/public/cpp",
     "//services/video_capture/public/mojom",
     "//services/video_capture/public/mojom:constants",
@@ -78,10 +61,6 @@
   sources = [
     "broadcasting_receiver_unittest.cc",
     "device_media_to_mojo_adapter_unittest.cc",
-    "test/device_factory_provider_connectortest.cc",
-    "test/device_factory_provider_test.cc",
-    "test/device_factory_provider_test.h",
-    "test/device_factory_provider_unittest.cc",
     "test/fake_device_descriptor_test.cc",
     "test/fake_device_descriptor_test.h",
     "test/fake_device_descriptor_unittest.cc",
@@ -94,26 +73,22 @@
     "test/mock_device_unittest.cc",
     "test/mock_devices_changed_observer.cc",
     "test/mock_devices_changed_observer.h",
+    "test/service_lifecycle_unittest.cc",
+    "test/video_capture_service_test.cc",
+    "test/video_capture_service_test.h",
+    "test/video_capture_service_unittest.cc",
     "test/virtual_device_unittest.cc",
     "texture_virtual_device_mojo_adapter_unittest.cc",
   ]
 
   deps = [
     ":lib",
-    ":video_capture",
     "//base/test:test_support",
     "//media/capture:test_support",
     "//media/capture/mojom:video_capture",
-    "//services/service_manager/public/cpp",
-    "//services/service_manager/public/cpp/test:test_support",
-    "//services/video_capture/public/cpp:manifest",
     "//services/video_capture/public/cpp:mocks",
     "//testing/gmock",
     "//testing/gtest",
     "//ui/gfx:test_support",
   ]
-
-  data_deps = [
-    ":video_capture",
-  ]
 }
diff --git a/services/video_capture/device_factory.h b/services/video_capture/device_factory.h
index 4fe8376..0898a1a 100644
--- a/services/video_capture/device_factory.h
+++ b/services/video_capture/device_factory.h
@@ -5,7 +5,6 @@
 #ifndef SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_H_
 #define SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_H_
 
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
 
 #if defined(OS_CHROMEOS)
@@ -16,8 +15,6 @@
 
 class DeviceFactory : public mojom::DeviceFactory {
  public:
-  virtual void SetServiceRef(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref) = 0;
 #if defined(OS_CHROMEOS)
   virtual void BindCrosImageCaptureRequest(
       cros::mojom::CrosImageCaptureRequest request) = 0;
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.cc b/services/video_capture/device_factory_media_to_mojo_adapter.cc
index 5b8e3f02d..59416448 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.cc
@@ -99,11 +99,6 @@
 
 DeviceFactoryMediaToMojoAdapter::~DeviceFactoryMediaToMojoAdapter() = default;
 
-void DeviceFactoryMediaToMojoAdapter::SetServiceRef(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref) {
-  service_ref_ = std::move(service_ref);
-}
-
 void DeviceFactoryMediaToMojoAdapter::GetDeviceInfos(
     GetDeviceInfosCallback callback) {
   capture_system_->GetDeviceInfosAsync(
@@ -170,7 +165,6 @@
     const std::string& device_id,
     mojom::DeviceRequest device_request,
     CreateDeviceCallback callback) {
-  DCHECK(service_ref_);
   std::unique_ptr<media::VideoCaptureDevice> media_device =
       capture_system_->CreateDevice(device_id);
   if (media_device == nullptr) {
@@ -184,11 +178,11 @@
 
 #if defined(OS_CHROMEOS)
   device_entry.device = std::make_unique<DeviceMediaToMojoAdapter>(
-      service_ref_->Clone(), std::move(media_device),
-      jpeg_decoder_factory_callback_, jpeg_decoder_task_runner_);
+      std::move(media_device), jpeg_decoder_factory_callback_,
+      jpeg_decoder_task_runner_);
 #else
-  device_entry.device = std::make_unique<DeviceMediaToMojoAdapter>(
-      service_ref_->Clone(), std::move(media_device));
+  device_entry.device =
+      std::make_unique<DeviceMediaToMojoAdapter>(std::move(media_device));
 #endif  // defined(OS_CHROMEOS)
   device_entry.binding = std::make_unique<mojo::Binding<mojom::Device>>(
       device_entry.device.get(), std::move(device_request));
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.h b/services/video_capture/device_factory_media_to_mojo_adapter.h
index 07db761..28095f25 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.h
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.h
@@ -10,7 +10,6 @@
 #include "media/capture/video/video_capture_device_client.h"
 #include "media/capture/video/video_capture_system.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/device_factory.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
 
@@ -41,8 +40,6 @@
   ~DeviceFactoryMediaToMojoAdapter() override;
 
   // DeviceFactory implementation.
-  void SetServiceRef(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref) override;
   void GetDeviceInfos(GetDeviceInfosCallback callback) override;
   void CreateDevice(const std::string& device_id,
                     mojom::DeviceRequest device_request,
@@ -83,7 +80,6 @@
                              CreateDeviceCallback callback);
   void OnClientConnectionErrorOrClose(const std::string& device_id);
 
-  std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   const std::unique_ptr<media::VideoCaptureSystem> capture_system_;
   std::map<std::string, ActiveDeviceEntry> active_devices_by_id_;
 
diff --git a/services/video_capture/device_factory_provider_impl.h b/services/video_capture/device_factory_provider_impl.h
deleted file mode 100644
index 950cd0b..0000000
--- a/services/video_capture/device_factory_provider_impl.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_PROVIDER_H_
-#define SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_PROVIDER_H_
-
-#include <memory>
-
-#include "base/memory/scoped_refptr.h"
-#include "base/threading/thread.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
-#include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
-
-#if defined(OS_CHROMEOS)
-#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
-#endif  // defined(OS_CHROMEOS)
-
-namespace video_capture {
-
-class VirtualDeviceEnabledDeviceFactory;
-class VideoSourceProviderImpl;
-
-class DeviceFactoryProviderImpl : public mojom::DeviceFactoryProvider {
- public:
-  explicit DeviceFactoryProviderImpl(
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-      base::OnceClosure request_service_quit_asap_cb);
-  ~DeviceFactoryProviderImpl() override;
-
-  void SetServiceRef(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
-
-  // mojom::DeviceFactoryProvider implementation.
-#if defined(OS_CHROMEOS)
-  void InjectGpuDependencies(
-      mojom::AcceleratorFactoryPtr accelerator_factory) override;
-#endif  // defined(OS_CHROMEOS)
-  void ConnectToDeviceFactory(mojom::DeviceFactoryRequest request) override;
-  void ConnectToVideoSourceProvider(
-      mojom::VideoSourceProviderRequest request) override;
-  void ShutdownServiceAsap() override;
-  void SetRetryCount(int32_t count) override;
-
-#if defined(OS_CHROMEOS)
-  void BindCrosImageCaptureRequest(
-      cros::mojom::CrosImageCaptureRequest request);
-#endif  // defined(OS_CHROMEOS)
-
- private:
-  class GpuDependenciesContext;
-
-  void LazyInitializeGpuDependenciesContext();
-  void LazyInitializeDeviceFactory();
-  void LazyInitializeVideoSourceProvider();
-  void OnLastSourceProviderClientDisconnected();
-  void OnFactoryOrSourceProviderClientDisconnected();
-
-  mojo::BindingSet<mojom::DeviceFactory> factory_bindings_;
-  std::unique_ptr<VirtualDeviceEnabledDeviceFactory> device_factory_;
-  std::unique_ptr<VideoSourceProviderImpl> video_source_provider_;
-  std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-  std::unique_ptr<GpuDependenciesContext> gpu_dependencies_context_;
-
-  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
-  base::OnceClosure request_service_quit_asap_cb_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceFactoryProviderImpl);
-};
-
-}  // namespace video_capture
-
-#endif  // SERVICES_VIDEO_CAPTURE_DEVICE_FACTORY_PROVIDER_H_
diff --git a/services/video_capture/device_media_to_mojo_adapter.cc b/services/video_capture/device_media_to_mojo_adapter.cc
index dfb0abd..1ce673c 100644
--- a/services/video_capture/device_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_media_to_mojo_adapter.cc
@@ -40,24 +40,18 @@
 
 #if defined(OS_CHROMEOS)
 DeviceMediaToMojoAdapter::DeviceMediaToMojoAdapter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref,
     std::unique_ptr<media::VideoCaptureDevice> device,
     media::MojoMjpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback,
     scoped_refptr<base::SequencedTaskRunner> jpeg_decoder_task_runner)
-    : service_ref_(std::move(service_ref)),
-      device_(std::move(device)),
+    : device_(std::move(device)),
       jpeg_decoder_factory_callback_(std::move(jpeg_decoder_factory_callback)),
       jpeg_decoder_task_runner_(std::move(jpeg_decoder_task_runner)),
       device_started_(false),
       weak_factory_(this) {}
 #else
 DeviceMediaToMojoAdapter::DeviceMediaToMojoAdapter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref,
     std::unique_ptr<media::VideoCaptureDevice> device)
-    : service_ref_(std::move(service_ref)),
-      device_(std::move(device)),
-      device_started_(false),
-      weak_factory_(this) {}
+    : device_(std::move(device)), device_started_(false), weak_factory_(this) {}
 #endif  // defined(OS_CHROMEOS)
 
 DeviceMediaToMojoAdapter::~DeviceMediaToMojoAdapter() {
diff --git a/services/video_capture/device_media_to_mojo_adapter.h b/services/video_capture/device_media_to_mojo_adapter.h
index 4ebf2e52..2e9d1ec 100644
--- a/services/video_capture/device_media_to_mojo_adapter.h
+++ b/services/video_capture/device_media_to_mojo_adapter.h
@@ -10,7 +10,6 @@
 #include "media/capture/video/video_capture_device_client.h"
 #include "media/capture/video/video_capture_device_factory.h"
 #include "media/capture/video_capture_types.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 
 #if defined(OS_CHROMEOS)
@@ -28,13 +27,11 @@
  public:
 #if defined(OS_CHROMEOS)
   DeviceMediaToMojoAdapter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref,
       std::unique_ptr<media::VideoCaptureDevice> device,
       media::MojoMjpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback,
       scoped_refptr<base::SequencedTaskRunner> jpeg_decoder_task_runner);
 #else
   DeviceMediaToMojoAdapter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref,
       std::unique_ptr<media::VideoCaptureDevice> device);
 #endif  // defined(OS_CHROMEOS)
   ~DeviceMediaToMojoAdapter() override;
@@ -57,7 +54,6 @@
   static int max_buffer_pool_buffer_count();
 
  private:
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   const std::unique_ptr<media::VideoCaptureDevice> device_;
 #if defined(OS_CHROMEOS)
   const media::MojoMjpegDecodeAcceleratorFactoryCB
diff --git a/services/video_capture/device_media_to_mojo_adapter_unittest.cc b/services/video_capture/device_media_to_mojo_adapter_unittest.cc
index 818df33..7ae494eb 100644
--- a/services/video_capture/device_media_to_mojo_adapter_unittest.cc
+++ b/services/video_capture/device_media_to_mojo_adapter_unittest.cc
@@ -29,12 +29,10 @@
     mock_device_ptr_ = mock_device.get();
 #if defined(OS_CHROMEOS)
     adapter_ = std::make_unique<DeviceMediaToMojoAdapter>(
-        std::unique_ptr<service_manager::ServiceContextRef>(),
         std::move(mock_device), base::DoNothing(),
         base::ThreadTaskRunnerHandle::Get());
 #else
     adapter_ = std::make_unique<DeviceMediaToMojoAdapter>(
-        std::unique_ptr<service_manager::ServiceContextRef>(),
         std::move(mock_device));
 #endif  // defined(OS_CHROMEOS)
   }
diff --git a/services/video_capture/public/cpp/BUILD.gn b/services/video_capture/public/cpp/BUILD.gn
index 07fdfdaa..98462de 100644
--- a/services/video_capture/public/cpp/BUILD.gn
+++ b/services/video_capture/public/cpp/BUILD.gn
@@ -14,7 +14,6 @@
     "//base",
     "//media",
     "//media/capture:capture",
-    "//services/service_manager/public/cpp",
     "//services/video_capture/public/mojom",
   ]
 
@@ -23,36 +22,20 @@
   ]
 }
 
-source_set("manifest") {
-  sources = [
-    "manifest.cc",
-    "manifest.h",
-  ]
-  deps = [
-    "//base",
-    "//services/service_manager/public/cpp",
-    "//services/video_capture/public/mojom",
-    "//services/video_capture/public/mojom:constants",
-  ]
-  if (is_chromeos) {
-    deps += [ "//media/capture/video/chromeos/mojo:cros_camera" ]
-  }
-}
-
 source_set("mocks") {
   testonly = true
 
   sources = [
     "mock_device_factory.cc",
     "mock_device_factory.h",
-    "mock_device_factory_provider.cc",
-    "mock_device_factory_provider.h",
     "mock_producer.cc",
     "mock_producer.h",
     "mock_push_subscription.cc",
     "mock_push_subscription.h",
     "mock_receiver.cc",
     "mock_receiver.h",
+    "mock_video_capture_service.cc",
+    "mock_video_capture_service.h",
     "mock_video_source.cc",
     "mock_video_source.h",
     "mock_video_source_provider.cc",
diff --git a/services/video_capture/public/cpp/manifest.cc b/services/video_capture/public/cpp/manifest.cc
deleted file mode 100644
index fc1b20d..0000000
--- a/services/video_capture/public/cpp/manifest.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/video_capture/public/cpp/manifest.h"
-
-#if defined(OS_CHROMEOS)
-#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
-#endif  // defined(OS_CHROMEOS)
-#include "services/service_manager/public/cpp/manifest_builder.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
-#include "services/video_capture/public/mojom/testing_controls.mojom.h"
-
-namespace video_capture {
-
-service_manager::Manifest GetManifest(
-    service_manager::Manifest::ExecutionMode execution_mode) {
-  return service_manager::Manifest {
-    service_manager::ManifestBuilder()
-        .WithServiceName(mojom::kServiceName)
-        .WithDisplayName("Video Capture")
-        .WithOptions(service_manager::ManifestOptionsBuilder()
-                         .WithExecutionMode(execution_mode)
-                         .WithSandboxType("none")
-                         .WithInstanceSharingPolicy(
-                             service_manager::Manifest::InstanceSharingPolicy::
-                                 kSharedAcrossGroups)
-                         .Build())
-        .ExposeCapability("capture", service_manager::Manifest::InterfaceList<
-#if defined(OS_CHROMEOS)
-                                         cros::mojom::CrosImageCapture,
-#endif  // defined(OS_CHROMEOS)
-                                         mojom::DeviceFactoryProvider>())
-        .ExposeCapability(
-            "tests",
-            service_manager::Manifest::InterfaceList<
-                mojom::DeviceFactoryProvider, mojom::TestingControls>())
-        .Build()
-  };
-}
-
-}  // namespace video_capture
diff --git a/services/video_capture/public/cpp/manifest.h b/services/video_capture/public/cpp/manifest.h
deleted file mode 100644
index d28ef4a..0000000
--- a/services/video_capture/public/cpp/manifest.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_
-#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_
-
-#include "services/service_manager/public/cpp/manifest.h"
-
-namespace video_capture {
-
-service_manager::Manifest GetManifest(
-    service_manager::Manifest::ExecutionMode execution_mode);
-
-}  // namespace video_capture
-
-#endif  // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MANIFEST_H_
diff --git a/services/video_capture/public/cpp/mock_device_factory_provider.cc b/services/video_capture/public/cpp/mock_video_capture_service.cc
similarity index 62%
rename from services/video_capture/public/cpp/mock_device_factory_provider.cc
rename to services/video_capture/public/cpp/mock_video_capture_service.cc
index 2dbc71c..f2dbb05b 100644
--- a/services/video_capture/public/cpp/mock_device_factory_provider.cc
+++ b/services/video_capture/public/cpp/mock_video_capture_service.cc
@@ -2,26 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/video_capture/public/cpp/mock_device_factory_provider.h"
+#include "services/video_capture/public/cpp/mock_video_capture_service.h"
 
 namespace video_capture {
 
-MockDeviceFactoryProvider::MockDeviceFactoryProvider() {}
+MockVideoCaptureService::MockVideoCaptureService() {}
 
-MockDeviceFactoryProvider::~MockDeviceFactoryProvider() = default;
+MockVideoCaptureService::~MockVideoCaptureService() = default;
 
-void MockDeviceFactoryProvider::ConnectToDeviceFactory(
+void MockVideoCaptureService::ConnectToDeviceFactory(
     video_capture::mojom::DeviceFactoryRequest request) {
   DoConnectToDeviceFactory(request);
 }
 
-void MockDeviceFactoryProvider::ConnectToVideoSourceProvider(
+void MockVideoCaptureService::ConnectToVideoSourceProvider(
     video_capture::mojom::VideoSourceProviderRequest request) {
   DoConnectToVideoSourceProvider(request);
 }
 
 #if defined(OS_CHROMEOS)
-void MockDeviceFactoryProvider::InjectGpuDependencies(
+void MockVideoCaptureService::InjectGpuDependencies(
     video_capture::mojom::AcceleratorFactoryPtr accelerator_factory) {
   DoInjectGpuDependencies(accelerator_factory);
 }
diff --git a/services/video_capture/public/cpp/mock_device_factory_provider.h b/services/video_capture/public/cpp/mock_video_capture_service.h
similarity index 63%
rename from services/video_capture/public/cpp/mock_device_factory_provider.h
rename to services/video_capture/public/cpp/mock_video_capture_service.h
index 645a75d..da63ced 100644
--- a/services/video_capture/public/cpp/mock_device_factory_provider.h
+++ b/services/video_capture/public/cpp/mock_video_capture_service.h
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_PROVIDER_H_
-#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_PROVIDER_H_
+#ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_CAPTURE_SERVICE_H_
+#define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_CAPTURE_SERVICE_H_
 
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace video_capture {
 
-class MockDeviceFactoryProvider
-    : public video_capture::mojom::DeviceFactoryProvider {
+class MockVideoCaptureService
+    : public video_capture::mojom::VideoCaptureService {
  public:
-  MockDeviceFactoryProvider();
-  ~MockDeviceFactoryProvider() override;
+  MockVideoCaptureService();
+  ~MockVideoCaptureService() override;
 
   void ConnectToDeviceFactory(
       video_capture::mojom::DeviceFactoryRequest request) override;
@@ -29,17 +29,22 @@
   MOCK_METHOD1(
       DoInjectGpuDependencies,
       void(video_capture::mojom::AcceleratorFactoryPtr& accelerator_factory));
+
+  void BindCrosImageCapture(
+      mojo::PendingReceiver<cros::mojom::CrosImageCapture>) override {}
 #endif  // defined(OS_CHROMEOS
 
+  void BindControlsForTesting(
+      mojo::PendingReceiver<mojom::TestingControls>) override {}
+
   MOCK_METHOD1(SetShutdownDelayInSeconds, void(float seconds));
   MOCK_METHOD1(DoConnectToDeviceFactory,
                void(video_capture::mojom::DeviceFactoryRequest& request));
   MOCK_METHOD1(DoConnectToVideoSourceProvider,
                void(video_capture::mojom::VideoSourceProviderRequest& request));
-  MOCK_METHOD0(ShutdownServiceAsap, void());
   MOCK_METHOD1(SetRetryCount, void(int32_t));
 };
 
 }  // namespace video_capture
 
-#endif  // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_PROVIDER_H_
+#endif  // SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_CAPTURE_SERVICE_H_
diff --git a/services/video_capture/public/mojom/BUILD.gn b/services/video_capture/public/mojom/BUILD.gn
index 908532a..11b2e39 100644
--- a/services/video_capture/public/mojom/BUILD.gn
+++ b/services/video_capture/public/mojom/BUILD.gn
@@ -8,12 +8,12 @@
   sources = [
     "device.mojom",
     "device_factory.mojom",
-    "device_factory_provider.mojom",
     "devices_changed_observer.mojom",
     "producer.mojom",
     "receiver.mojom",
     "scoped_access_permission.mojom",
     "testing_controls.mojom",
+    "video_capture_service.mojom",
     "video_source.mojom",
     "video_source_provider.mojom",
     "virtual_device.mojom",
@@ -27,7 +27,10 @@
   ]
 
   if (is_chromeos) {
-    deps += [ "//components/chromeos_camera/common" ]
+    deps += [
+      "//components/chromeos_camera/common",
+      "//media/capture/video/chromeos/mojo:cros_camera",
+    ]
   }
 }
 
diff --git a/services/video_capture/public/mojom/constants.mojom b/services/video_capture/public/mojom/constants.mojom
index ee5cd3e..d13930e 100644
--- a/services/video_capture/public/mojom/constants.mojom
+++ b/services/video_capture/public/mojom/constants.mojom
@@ -4,5 +4,4 @@
 
 module video_capture.mojom;
 
-const string kServiceName = "video_capture";
 const int32 kInvalidBufferId = -1;
diff --git a/services/video_capture/public/mojom/device_factory_provider.mojom b/services/video_capture/public/mojom/video_capture_service.mojom
similarity index 78%
rename from services/video_capture/public/mojom/device_factory_provider.mojom
rename to services/video_capture/public/mojom/video_capture_service.mojom
index 4ec2285..916e8ef 100644
--- a/services/video_capture/public/mojom/device_factory_provider.mojom
+++ b/services/video_capture/public/mojom/video_capture_service.mojom
@@ -7,9 +7,13 @@
 [EnableIf=is_chromeos]
 import "components/chromeos_camera/common/mjpeg_decode_accelerator.mojom";
 import "services/video_capture/public/mojom/device_factory.mojom";
+import "services/video_capture/public/mojom/testing_controls.mojom";
 import "services/video_capture/public/mojom/video_source_provider.mojom";
 
 [EnableIf=is_chromeos]
+import "media/capture/video/chromeos/mojo/cros_image_capture.mojom";
+
+[EnableIf=is_chromeos]
 interface AcceleratorFactory {
   // Creates a new JpegDecodeAccelerator and binds it to |jda|.
   CreateJpegDecodeAccelerator(
@@ -25,22 +29,24 @@
 // to be called once before any call to ConnectToDeviceFactory() is made.
 // Calling InjectGpuDependencies() is optional. If it is not called, MJPEG
 // decoding will be performed without gpu acceleration.
-// TODO(chfremer): Consider renaming this to VideoCaptureService.
-interface DeviceFactoryProvider  {
+interface VideoCaptureService  {
   [EnableIf=is_chromeos]
   InjectGpuDependencies(AcceleratorFactory accelerator_factory);
 
+  // Binds a Chrome OS camera capture interface.
+  [EnableIf=is_chromeos]
+  BindCrosImageCapture(pending_receiver<cros.mojom.CrosImageCapture> receiver);
+
   // Legacy API. Supports one client per device.
   ConnectToDeviceFactory(DeviceFactory& request);
 
   // Current API. Supports multiple clients per source.
   ConnectToVideoSourceProvider(VideoSourceProvider& request);
 
-  // Lets the service to close all connections and ask the service_manager to be
-  // shut down.
-  ShutdownServiceAsap();
-
   // Sets a retry count that is used by the service for logging UMA events in
   // the context of investigation for https://crbug.com/643068.
   SetRetryCount(int32 count);
+
+  // Binds a test-only interface for use by unit tests.
+  BindControlsForTesting(pending_receiver<TestingControls> receiver);
 };
diff --git a/services/video_capture/service_impl.cc b/services/video_capture/service_impl.cc
deleted file mode 100644
index 5bf1432..0000000
--- a/services/video_capture/service_impl.cc
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/video_capture/service_impl.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "build/build_config.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "services/video_capture/device_factory_provider_impl.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
-#include "services/video_capture/public/uma/video_capture_service_event.h"
-#include "services/video_capture/testing_controls_impl.h"
-
-#if defined(OS_CHROMEOS)
-#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
-#endif  // defined(OS_CHROMEOS)
-
-namespace video_capture {
-
-namespace {
-
-#if defined(OS_ANDROID)
-// On Android, we do not use automatic service shutdown, because when shutting
-// down the service, we lose caching of the supported formats, and re-querying
-// these can take several seconds on certain Android devices.
-constexpr base::Optional<base::TimeDelta> kDefaultIdleTimeout;
-#else
-constexpr base::Optional<base::TimeDelta> kDefaultIdleTimeout =
-    base::TimeDelta::FromSeconds(5);
-#endif
-
-}  // namespace
-
-ServiceImpl::ServiceImpl(
-    service_manager::mojom::ServiceRequest request,
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
-    : ServiceImpl(std::move(request),
-                  std::move(ui_task_runner),
-                  kDefaultIdleTimeout) {}
-
-ServiceImpl::ServiceImpl(
-    service_manager::mojom::ServiceRequest request,
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-    base::Optional<base::TimeDelta> idle_timeout)
-    : binding_(this, std::move(request)),
-      keepalive_(&binding_, idle_timeout),
-      ui_task_runner_(std::move(ui_task_runner)) {
-  keepalive_.AddObserver(this);
-}
-
-ServiceImpl::~ServiceImpl() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  keepalive_.RemoveObserver(this);
-}
-
-void ServiceImpl::SetFactoryProviderClientConnectedObserver(
-    base::RepeatingClosure observer_cb) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  factory_provider_client_connected_cb_ = std::move(observer_cb);
-}
-
-void ServiceImpl::SetFactoryProviderClientDisconnectedObserver(
-    base::RepeatingClosure observer_cb) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  factory_provider_client_disconnected_cb_ = std::move(observer_cb);
-}
-
-void ServiceImpl::SetShutdownTimeoutCancelledObserver(
-    base::RepeatingClosure observer_cb) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  shutdown_timeout_cancelled_cb_ = std::move(observer_cb);
-}
-
-bool ServiceImpl::HasNoContextRefs() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return keepalive_.HasNoRefs();
-}
-
-void ServiceImpl::ShutdownServiceAsap() {
-  binding_.RequestClose();
-}
-
-void ServiceImpl::OnStart() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  video_capture::uma::LogVideoCaptureServiceEvent(
-      video_capture::uma::SERVICE_STARTED);
-
-  registry_.AddInterface<mojom::DeviceFactoryProvider>(
-      // Unretained |this| is safe because |registry_| is owned by |this|.
-      base::BindRepeating(&ServiceImpl::OnDeviceFactoryProviderRequest,
-                          base::Unretained(this)));
-  registry_.AddInterface<mojom::TestingControls>(
-      // Unretained |this| is safe because |registry_| is owned by |this|.
-      base::BindRepeating(&ServiceImpl::OnTestingControlsRequest,
-                          base::Unretained(this)));
-
-#if defined(OS_CHROMEOS)
-  registry_.AddInterface<cros::mojom::CrosImageCapture>(base::BindRepeating(
-      &ServiceImpl::OnCrosImageCaptureRequest, base::Unretained(this)));
-#endif  // defined(OS_CHROMEOS)
-
-  // Unretained |this| is safe because |factory_provider_bindings_| is owned by
-  // |this|.
-  factory_provider_bindings_.set_connection_error_handler(base::BindRepeating(
-      &ServiceImpl::OnProviderClientDisconnected, base::Unretained(this)));
-}
-
-void ServiceImpl::OnBindInterface(
-    const service_manager::BindSourceInfo& source_info,
-    const std::string& interface_name,
-    mojo::ScopedMessagePipeHandle interface_pipe) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  registry_.BindInterface(interface_name, std::move(interface_pipe));
-}
-
-bool ServiceImpl::OnServiceManagerConnectionLost() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return true;
-}
-
-void ServiceImpl::OnIdleTimeout() {
-  video_capture::uma::LogVideoCaptureServiceEvent(
-      video_capture::uma::SERVICE_SHUTTING_DOWN_BECAUSE_NO_CLIENT);
-}
-
-void ServiceImpl::OnIdleTimeoutCancelled() {
-  video_capture::uma::LogVideoCaptureServiceEvent(
-      video_capture::uma::SERVICE_SHUTDOWN_TIMEOUT_CANCELED);
-  if (shutdown_timeout_cancelled_cb_)
-    shutdown_timeout_cancelled_cb_.Run();
-}
-
-void ServiceImpl::OnDeviceFactoryProviderRequest(
-    mojom::DeviceFactoryProviderRequest request) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  LazyInitializeDeviceFactoryProvider();
-  if (factory_provider_bindings_.empty())
-    device_factory_provider_->SetServiceRef(keepalive_.CreateRef());
-  factory_provider_bindings_.AddBinding(device_factory_provider_.get(),
-                                        std::move(request));
-
-  if (!factory_provider_client_connected_cb_.is_null()) {
-    factory_provider_client_connected_cb_.Run();
-  }
-}
-
-void ServiceImpl::OnTestingControlsRequest(
-    mojom::TestingControlsRequest request) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  mojo::MakeStrongBinding(
-      std::make_unique<TestingControlsImpl>(keepalive_.CreateRef()),
-      std::move(request));
-}
-
-#if defined(OS_CHROMEOS)
-void ServiceImpl::OnCrosImageCaptureRequest(
-    cros::mojom::CrosImageCaptureRequest request) {
-  LazyInitializeDeviceFactoryProvider();
-  device_factory_provider_->BindCrosImageCaptureRequest(std::move(request));
-}
-#endif  // defined(OS_CHROMEOS)
-
-void ServiceImpl::LazyInitializeDeviceFactoryProvider() {
-  if (device_factory_provider_)
-    return;
-
-  // Use of base::Unretained() is safe because |this| owns, and therefore
-  // outlives |device_factory_provider_|
-  device_factory_provider_ = std::make_unique<DeviceFactoryProviderImpl>(
-      ui_task_runner_, base::BindOnce(&ServiceImpl::ShutdownServiceAsap,
-                                      base::Unretained(this)));
-}
-
-void ServiceImpl::OnProviderClientDisconnected() {
-  // If last client has disconnected, release service ref so that service
-  // shutdown timeout starts if no other references are still alive.
-  // We keep the |device_factory_provider_| instance alive in order to avoid
-  // losing state that would be expensive to reinitialize, e.g. having
-  // already enumerated the available devices.
-  if (factory_provider_bindings_.empty())
-    device_factory_provider_->SetServiceRef(nullptr);
-
-  if (!factory_provider_client_disconnected_cb_.is_null()) {
-    factory_provider_client_disconnected_cb_.Run();
-  }
-}
-
-}  // namespace video_capture
diff --git a/services/video_capture/service_impl.h b/services/video_capture/service_impl.h
deleted file mode 100644
index 65b2a12a..0000000
--- a/services/video_capture/service_impl.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_VIDEO_CAPTURE_SERVICE_IMPL_H_
-#define SERVICES_VIDEO_CAPTURE_SERVICE_IMPL_H_
-
-#include <memory>
-
-#include "base/memory/scoped_refptr.h"
-#include "base/optional.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/time.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
-#include "services/video_capture/device_factory_provider_impl.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
-#include "services/video_capture/public/mojom/testing_controls.mojom.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#endif
-
-#if defined(OS_CHROMEOS)
-#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
-#endif  // defined(OS_CHROMEOS)
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace video_capture {
-
-class ServiceImpl : public service_manager::Service,
-                    public service_manager::ServiceKeepalive::Observer {
- public:
-  ServiceImpl(service_manager::mojom::ServiceRequest request,
-              scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
-
-  // Constructs a service instance which overrides the default idle timeout
-  // behavior.
-  ServiceImpl(service_manager::mojom::ServiceRequest request,
-              scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-              base::Optional<base::TimeDelta> idle_timeout);
-
-  ~ServiceImpl() override;
-
-  void SetFactoryProviderClientConnectedObserver(
-      base::RepeatingClosure observer_cb);
-  void SetFactoryProviderClientDisconnectedObserver(
-      base::RepeatingClosure observer_cb);
-  void SetShutdownTimeoutCancelledObserver(base::RepeatingClosure observer_cb);
-  bool HasNoContextRefs();
-
-  void ShutdownServiceAsap();
-
-  // service_manager::Service implementation.
-  void OnStart() override;
-  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
-                       const std::string& interface_name,
-                       mojo::ScopedMessagePipeHandle interface_pipe) override;
-  bool OnServiceManagerConnectionLost() override;
-
-  // service_manager::ServiceKeepalive::Observer implementation.
-  void OnIdleTimeout() override;
-  void OnIdleTimeoutCancelled() override;
-
- private:
-  void OnDeviceFactoryProviderRequest(
-      mojom::DeviceFactoryProviderRequest request);
-  void OnTestingControlsRequest(mojom::TestingControlsRequest request);
-#if defined(OS_CHROMEOS)
-  void OnCrosImageCaptureRequest(cros::mojom::CrosImageCaptureRequest request);
-#endif  // defined(OS_CHROMEOS)
-  void MaybeRequestQuitDelayed();
-  void MaybeRequestQuit();
-  void LazyInitializeDeviceFactoryProvider();
-  void OnProviderClientDisconnected();
-
-  service_manager::ServiceBinding binding_;
-  service_manager::ServiceKeepalive keepalive_;
-  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
-
-#if defined(OS_WIN)
-  // COM must be initialized in order to access the video capture devices.
-  base::win::ScopedCOMInitializer com_initializer_;
-#endif
-  service_manager::BinderRegistry registry_;
-  mojo::BindingSet<mojom::DeviceFactoryProvider> factory_provider_bindings_;
-  std::unique_ptr<DeviceFactoryProviderImpl> device_factory_provider_;
-
-  // Callbacks that can optionally be set by clients.
-  base::RepeatingClosure factory_provider_client_connected_cb_;
-  base::RepeatingClosure factory_provider_client_disconnected_cb_;
-  base::RepeatingClosure shutdown_timeout_cancelled_cb_;
-
-  base::ThreadChecker thread_checker_;
-
-  DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
-};
-
-}  // namespace video_capture
-
-#endif  // SERVICES_VIDEO_CAPTURE_SERVICE_IMPL_H_
diff --git a/services/video_capture/service_main.cc b/services/video_capture/service_main.cc
deleted file mode 100644
index 77f46359..0000000
--- a/services/video_capture/service_main.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/service_manager/public/cpp/service_executable/service_main.h"
-#include "base/task/single_thread_task_executor.h"
-#include "services/video_capture/service_impl.h"
-
-void ServiceMain(service_manager::mojom::ServiceRequest request) {
-  base::SingleThreadTaskExecutor main_thread_task_executor;
-  video_capture::ServiceImpl(std::move(request),
-                             main_thread_task_executor.task_runner())
-      .RunUntilTermination();
-}
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
index 2cecb0fe..265ed01 100644
--- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
+++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
@@ -29,11 +29,9 @@
 namespace video_capture {
 
 SharedMemoryVirtualDeviceMojoAdapter::SharedMemoryVirtualDeviceMojoAdapter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref,
     mojom::ProducerPtr producer,
     bool send_buffer_handles_to_producer_as_raw_file_descriptors)
-    : service_ref_(std::move(service_ref)),
-      producer_(std::move(producer)),
+    : producer_(std::move(producer)),
       send_buffer_handles_to_producer_as_raw_file_descriptors_(
           send_buffer_handles_to_producer_as_raw_file_descriptors),
       buffer_pool_(new media::VideoCaptureBufferPoolImpl(
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
index 45c4a2d4..4f35010 100644
--- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
+++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
@@ -8,7 +8,6 @@
 #include "base/sequence_checker.h"
 #include "media/capture/video/video_capture_buffer_pool.h"
 #include "media/capture/video_capture_types.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "services/video_capture/public/mojom/receiver.mojom.h"
@@ -21,7 +20,6 @@
       public mojom::Device {
  public:
   SharedMemoryVirtualDeviceMojoAdapter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref,
       mojom::ProducerPtr producer,
       bool send_buffer_handles_to_producer_as_raw_file_descriptors = false);
   ~SharedMemoryVirtualDeviceMojoAdapter() override;
@@ -54,7 +52,6 @@
  private:
   void OnReceiverConnectionErrorOrClose();
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   mojom::ReceiverPtr receiver_;
   mojom::ProducerPtr producer_;
   const bool send_buffer_handles_to_producer_as_raw_file_descriptors_;
diff --git a/services/video_capture/test/device_factory_provider_connectortest.cc b/services/video_capture/test/device_factory_provider_connectortest.cc
deleted file mode 100644
index 507e912..0000000
--- a/services/video_capture/test/device_factory_provider_connectortest.cc
+++ /dev/null
@@ -1,403 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/run_loop.h"
-#include "base/test/mock_callback.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/timer/timer.h"
-#include "media/base/media_switches.h"
-#include "services/service_manager/public/cpp/test/test_connector_factory.h"
-#include "services/video_capture/public/cpp/mock_receiver.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
-#include "services/video_capture/public/mojom/device.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
-#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
-#include "services/video_capture/service_impl.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace video_capture {
-
-using testing::Exactly;
-using testing::_;
-using testing::Invoke;
-using testing::InvokeWithoutArgs;
-
-// Test fixture that creates a video_capture::ServiceImpl and sets up a
-// local service_manager::Connector through which client code can connect to
-// it.
-template <class DeviceFactoryProviderConnectorTestTraits>
-class DeviceFactoryProviderConnectorTest : public ::testing::Test {
- public:
-  DeviceFactoryProviderConnectorTest() {}
-  ~DeviceFactoryProviderConnectorTest() override {}
-
-  void SetUp() override {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(
-        switches::kUseFakeDeviceForMediaStream);
-    service_impl_ = std::make_unique<ServiceImpl>(
-        connector_factory_.RegisterInstance(video_capture::mojom::kServiceName),
-        scoped_task_environment_.GetMainThreadTaskRunner(),
-        DeviceFactoryProviderConnectorTestTraits::shutdown_delay());
-    service_impl_->set_termination_closure(
-        base::BindOnce(&DeviceFactoryProviderConnectorTest::OnServiceQuit,
-                       base::Unretained(this)));
-
-    connector_ = connector_factory_.CreateConnector();
-
-    base::RunLoop wait_loop;
-    service_impl_->SetFactoryProviderClientConnectedObserver(
-        wait_loop.QuitClosure());
-    connector_->BindInterface(mojom::kServiceName, &factory_provider_);
-    wait_loop.Run();
-  }
-
-  void TearDown() override {
-    if (factory_provider_.is_bound() &&
-        DeviceFactoryProviderConnectorTestTraits::shutdown_delay()
-            .has_value()) {
-      factory_provider_.reset();
-      service_destroyed_wait_loop_.Run();
-    }
-  }
-
- protected:
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  std::unique_ptr<ServiceImpl> service_impl_;
-  mojom::DeviceFactoryProviderPtr factory_provider_;
-  base::MockCallback<mojom::DeviceFactory::GetDeviceInfosCallback>
-      device_info_receiver_;
-  std::unique_ptr<service_manager::Connector> connector_;
-  base::RunLoop service_destroyed_wait_loop_;
-  base::OnceClosure service_quit_callback_;
-
- private:
-  void OnServiceQuit() {
-    service_impl_.reset();
-    service_destroyed_wait_loop_.Quit();
-    if (service_quit_callback_)
-      std::move(service_quit_callback_).Run();
-  }
-
-  service_manager::TestConnectorFactory connector_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceFactoryProviderConnectorTest);
-};
-
-// We need to set the shutdown delay to at least some epsilon > 0 in order
-// to avoid the service shutting down synchronously which would prevent
-// test case ServiceIncreasesRefCountOnNewConnectionAfterDisconnect from
-// being able to reconnect before the timer expires.
-struct ShortShutdownDelayDeviceFactoryProviderConnectorTestTraits {
-  static base::Optional<base::TimeDelta> shutdown_delay() {
-    return base::TimeDelta::FromMicroseconds(100);
-  }
-};
-using ShortShutdownDelayDeviceFactoryProviderConnectorTest =
-    DeviceFactoryProviderConnectorTest<
-        ShortShutdownDelayDeviceFactoryProviderConnectorTestTraits>;
-
-// Tests that the service does not quit when a client connects
-// while a second client stays connected.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceDoesNotQuitWhenOneOfTwoClientsDisconnects) {
-  // Establish second connection
-  mojom::DeviceFactoryProviderPtr second_connection;
-  {
-    base::RunLoop wait_loop;
-    service_impl_->SetFactoryProviderClientConnectedObserver(
-        wait_loop.QuitClosure());
-    connector_->BindInterface(mojom::kServiceName, &second_connection);
-    wait_loop.Run();
-  }
-
-  // Release first connection
-  {
-    base::RunLoop wait_loop;
-    service_impl_->SetFactoryProviderClientDisconnectedObserver(
-        wait_loop.QuitClosure());
-    factory_provider_.reset();
-    wait_loop.Run();
-  }
-
-  EXPECT_FALSE(service_impl_->HasNoContextRefs());
-  EXPECT_FALSE(factory_provider_.is_bound());
-  EXPECT_TRUE(second_connection.is_bound());
-}
-
-// Tests that the service quits when the only client disconnects after not
-// having done anything other than obtaining a connection to the device factory.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceQuitsWhenSingleDeviceFactoryClientDisconnected) {
-  mojom::DeviceFactoryPtr factory;
-  factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
-  factory.reset();
-  factory_provider_.reset();
-
-  service_destroyed_wait_loop_.Run();
-}
-
-// Tests that the service quits when the only client disconnects after not
-// having done anything other than obtaining a connection to the video source
-// provider.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceQuitsWhenSingleVideoSourceProviderClientDisconnected) {
-  mojom::VideoSourceProviderPtr source_provider;
-  factory_provider_->ConnectToVideoSourceProvider(
-      mojo::MakeRequest(&source_provider));
-  source_provider.reset();
-  factory_provider_.reset();
-
-  service_destroyed_wait_loop_.Run();
-}
-
-// Tests that the service quits when the only client disconnects after
-// enumerating devices via the video source provider.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceQuitsAfterEnumeratingDevices) {
-  mojom::VideoSourceProviderPtr source_provider;
-  factory_provider_->ConnectToVideoSourceProvider(
-      mojo::MakeRequest(&source_provider));
-
-  base::RunLoop wait_loop;
-  EXPECT_CALL(device_info_receiver_, Run(_))
-      .WillOnce(
-          Invoke([&wait_loop](
-                     const std::vector<media::VideoCaptureDeviceInfo>& infos) {
-            wait_loop.Quit();
-          }));
-  source_provider->GetSourceInfos(device_info_receiver_.Get());
-  wait_loop.Run();
-
-  source_provider.reset();
-  factory_provider_.reset();
-
-  service_destroyed_wait_loop_.Run();
-}
-
-// Tests that enumerating devices works after the only client disconnects and
-// reconnects via the video source provider.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       EnumerateDevicesAfterReconnect) {
-  // Connect |source_provider|.
-  mojom::VideoSourceProviderPtr source_provider;
-  factory_provider_->ConnectToVideoSourceProvider(
-      mojo::MakeRequest(&source_provider));
-
-  // Disconnect |source_provider| and wait for the disconnect to propagate to
-  // the service.
-  {
-    base::RunLoop wait_loop;
-    source_provider->Close(base::BindOnce(
-        [](base::RunLoop* wait_loop) { wait_loop->Quit(); }, &wait_loop));
-    wait_loop.Run();
-    source_provider.reset();
-  }
-
-  // Reconnect |source_provider|.
-  factory_provider_->ConnectToVideoSourceProvider(
-      mojo::MakeRequest(&source_provider));
-
-  // Enumerate devices.
-  base::RunLoop wait_loop;
-  EXPECT_CALL(device_info_receiver_, Run(_))
-      .WillOnce(
-          Invoke([&wait_loop](
-                     const std::vector<media::VideoCaptureDeviceInfo>& infos) {
-            wait_loop.Quit();
-          }));
-  source_provider->GetSourceInfos(device_info_receiver_.Get());
-  wait_loop.Run();
-
-  source_provider.reset();
-  factory_provider_.reset();
-
-  service_destroyed_wait_loop_.Run();
-}
-
-// Tests that the service quits when both of two clients disconnect.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceQuitsWhenAllClientsDisconnected) {
-  // Bind another client to the DeviceFactoryProvider interface.
-  mojom::DeviceFactoryProviderPtr second_connection;
-  connector_->BindInterface(mojom::kServiceName, &second_connection);
-
-  mojom::DeviceFactoryPtr factory;
-  factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
-  factory.reset();
-  factory_provider_.reset();
-  second_connection.reset();
-
-  service_destroyed_wait_loop_.Run();
-}
-
-// Tests that the service increase the context ref count when a new connection
-// comes in after all previous connections have been released.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceIncreasesRefCountOnNewConnectionAfterDisconnect) {
-  base::RunLoop wait_loop;
-  service_impl_->SetShutdownTimeoutCancelledObserver(base::BindRepeating(
-      [](base::RunLoop* wait_loop) { wait_loop->Quit(); }, &wait_loop));
-  // Disconnect and reconnect
-  factory_provider_.reset();
-  connector_->BindInterface(mojom::kServiceName, &factory_provider_);
-  wait_loop.Run();
-
-  EXPECT_FALSE(service_impl_->HasNoContextRefs());
-}
-
-// Tests that the service quits when the last client disconnects while using a
-// device.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       ServiceQuitsWhenClientDisconnectsWhileUsingDevice) {
-  mojom::DeviceFactoryPtr factory;
-  factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
-
-  // Connect to and start first device (in this case a fake camera).
-  media::VideoCaptureDeviceInfo fake_device_info;
-  {
-    base::RunLoop wait_loop;
-    EXPECT_CALL(device_info_receiver_, Run(_))
-        .WillOnce(Invoke(
-            [&fake_device_info, &wait_loop](
-                const std::vector<media::VideoCaptureDeviceInfo>& infos) {
-              fake_device_info = infos[0];
-              wait_loop.Quit();
-            }));
-    factory->GetDeviceInfos(device_info_receiver_.Get());
-    wait_loop.Run();
-  }
-  mojom::DevicePtr fake_device;
-  factory->CreateDevice(
-      std::move(fake_device_info.descriptor.device_id),
-      mojo::MakeRequest(&fake_device),
-      base::BindOnce([](mojom::DeviceAccessResultCode result_code) {
-        ASSERT_EQ(mojom::DeviceAccessResultCode::SUCCESS, result_code);
-      }));
-  media::VideoCaptureParams requestable_settings;
-  requestable_settings.requested_format = fake_device_info.supported_formats[0];
-  mojom::ReceiverPtr receiver_proxy;
-  MockReceiver mock_receiver(mojo::MakeRequest(&receiver_proxy));
-  fake_device->Start(requestable_settings, std::move(receiver_proxy));
-  {
-    base::RunLoop wait_loop;
-    EXPECT_CALL(mock_receiver, OnStarted()).WillOnce([&wait_loop]() {
-      wait_loop.Quit();
-    });
-    wait_loop.Run();
-  }
-
-  // Disconnect
-  fake_device.reset();
-  factory.reset();
-  factory_provider_.reset();
-
-  service_destroyed_wait_loop_.Run();
-}
-
-// Tests that the service does not quit when the only client discards the
-// DeviceFactoryProvider but holds on to a DeviceFactory.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       DeviceFactoryCanStillBeUsedAfterReleaseingDeviceFactoryProvider) {
-  mojom::DeviceFactoryPtr factory;
-  factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
-
-  // Exercise: Disconnect DeviceFactoryProvider
-  {
-    base::RunLoop wait_loop;
-    service_impl_->SetFactoryProviderClientDisconnectedObserver(
-        wait_loop.QuitClosure());
-    factory_provider_.reset();
-    wait_loop.Run();
-  }
-
-  EXPECT_FALSE(service_impl_->HasNoContextRefs());
-
-  // Verify that |factory| is still functional by calling GetDeviceInfos().
-  {
-    base::RunLoop wait_loop;
-    EXPECT_CALL(device_info_receiver_, Run(_))
-        .WillOnce(Invoke(
-            [&wait_loop](
-                const std::vector<media::VideoCaptureDeviceInfo>& infos) {
-              wait_loop.Quit();
-            }));
-    factory->GetDeviceInfos(device_info_receiver_.Get());
-    wait_loop.Run();
-  }
-}
-
-// Tests that the service does not quit when the only client discards the
-// DeviceFactoryProvider but holds on to a VideoSourceProvider.
-TEST_F(ShortShutdownDelayDeviceFactoryProviderConnectorTest,
-       VideoSourceProviderCanStillBeUsedAfterReleaseingDeviceFactoryProvider) {
-  mojom::VideoSourceProviderPtr source_provider;
-  factory_provider_->ConnectToVideoSourceProvider(
-      mojo::MakeRequest(&source_provider));
-
-  // Exercise: Disconnect DeviceFactoryProvider
-  {
-    base::RunLoop wait_loop;
-    service_impl_->SetFactoryProviderClientDisconnectedObserver(
-        wait_loop.QuitClosure());
-    factory_provider_.reset();
-    wait_loop.Run();
-  }
-
-  EXPECT_FALSE(service_impl_->HasNoContextRefs());
-
-  // Verify that |source_provider| is still functional by calling
-  // GetDeviceInfos().
-  {
-    base::RunLoop wait_loop;
-    EXPECT_CALL(device_info_receiver_, Run(_))
-        .WillOnce(Invoke(
-            [&wait_loop](
-                const std::vector<media::VideoCaptureDeviceInfo>& infos) {
-              wait_loop.Quit();
-            }));
-    source_provider->GetSourceInfos(device_info_receiver_.Get());
-    wait_loop.Run();
-  }
-}
-
-struct NoAutomaticShutdownDeviceFactoryProviderConnectorTestTraits {
-  static base::Optional<base::TimeDelta> shutdown_delay() {
-    return base::Optional<base::TimeDelta>();
-  }
-};
-using NoAutomaticShutdownDeviceFactoryProviderConnectorTest =
-    DeviceFactoryProviderConnectorTest<
-        NoAutomaticShutdownDeviceFactoryProviderConnectorTestTraits>;
-
-// Tests that the service does not shut down after disconnecting.
-TEST_F(NoAutomaticShutdownDeviceFactoryProviderConnectorTest,
-       ServiceDoesNotShutDownOnDisconnect) {
-  {
-    base::RunLoop wait_loop;
-    service_impl_->SetFactoryProviderClientDisconnectedObserver(
-        wait_loop.QuitClosure());
-    factory_provider_.reset();
-    wait_loop.Run();
-  }
-
-  base::MockCallback<base::OnceClosure> service_impl_destructor_cb;
-  service_quit_callback_ = service_impl_destructor_cb.Get();
-  EXPECT_CALL(service_impl_destructor_cb, Run()).Times(0);
-
-  // Wait for an arbitrary short extra time after which we are convinced that
-  // there is enough evidence that service is not going to shut down.
-  {
-    base::RunLoop wait_loop;
-    base::OneShotTimer wait_timer;
-    wait_timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
-                     wait_loop.QuitClosure());
-    wait_loop.Run();
-  }
-
-  service_quit_callback_.Reset();
-}
-
-}  // namespace video_capture
diff --git a/services/video_capture/test/device_factory_provider_test.cc b/services/video_capture/test/device_factory_provider_test.cc
deleted file mode 100644
index 32333a20..0000000
--- a/services/video_capture/test/device_factory_provider_test.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/video_capture/test/device_factory_provider_test.h"
-
-#include "base/command_line.h"
-#include "media/base/media_switches.h"
-#include "services/service_manager/public/cpp/manifest_builder.h"
-#include "services/service_manager/public/mojom/constants.mojom.h"
-#include "services/service_manager/public/mojom/service_manager.mojom.h"
-#include "services/video_capture/public/cpp/manifest.h"
-#include "services/video_capture/public/cpp/mock_producer.h"
-#include "services/video_capture/public/mojom/constants.mojom.h"
-
-namespace video_capture {
-
-const char kTestServiceName[] = "video_capture_unittests";
-
-service_manager::Manifest MakeVideoCaptureManifestForUnsandboxedExecutable() {
-  service_manager::Manifest manifest(GetManifest(
-      service_manager::Manifest::ExecutionMode::kStandaloneExecutable));
-  manifest.options.sandbox_type = "none";
-  return manifest;
-}
-
-DeviceFactoryProviderTest::SharedMemoryVirtualDeviceContext::
-    SharedMemoryVirtualDeviceContext(mojom::ProducerRequest producer_request)
-    : mock_producer(
-          std::make_unique<MockProducer>(std::move(producer_request))) {}
-
-DeviceFactoryProviderTest::SharedMemoryVirtualDeviceContext::
-    ~SharedMemoryVirtualDeviceContext() = default;
-
-DeviceFactoryProviderTest::DeviceFactoryProviderTest()
-    : test_service_manager_(
-          {MakeVideoCaptureManifestForUnsandboxedExecutable(),
-           service_manager::ManifestBuilder()
-               .WithServiceName(kTestServiceName)
-               .RequireCapability(mojom::kServiceName, "tests")
-               .RequireCapability(service_manager::mojom::kServiceName,
-                                  "service_manager:service_manager")
-               .Build()}),
-      test_service_binding_(
-          &test_service_,
-          test_service_manager_.RegisterTestInstance(kTestServiceName)) {}
-
-DeviceFactoryProviderTest::~DeviceFactoryProviderTest() = default;
-
-void DeviceFactoryProviderTest::SetUp() {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kUseFakeMjpegDecodeAccelerator);
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kUseFakeDeviceForMediaStream, "device-count=3");
-
-  connector()->BindInterface(mojom::kServiceName, &factory_provider_);
-  // Note, that we explicitly do *not* call
-  // |factory_provider_->InjectGpuDependencies()| here. Test case
-  // |FakeMjpegVideoCaptureDeviceTest.
-  //  CanDecodeMjpegWithoutInjectedGpuDependencies| depends on this assumption.
-  factory_provider_->ConnectToDeviceFactory(mojo::MakeRequest(&factory_));
-}
-
-std::unique_ptr<DeviceFactoryProviderTest::SharedMemoryVirtualDeviceContext>
-DeviceFactoryProviderTest::AddSharedMemoryVirtualDevice(
-    const std::string& device_id) {
-  media::VideoCaptureDeviceInfo device_info;
-  device_info.descriptor.device_id = device_id;
-  mojom::ProducerPtr producer;
-  auto result = std::make_unique<SharedMemoryVirtualDeviceContext>(
-      mojo::MakeRequest(&producer));
-  factory_->AddSharedMemoryVirtualDevice(
-      device_info, std::move(producer),
-      false /* send_buffer_handles_to_producer_as_raw_file_descriptors */,
-      mojo::MakeRequest(&result->device));
-  return result;
-}
-
-mojom::TextureVirtualDevicePtr
-DeviceFactoryProviderTest::AddTextureVirtualDevice(
-    const std::string& device_id) {
-  media::VideoCaptureDeviceInfo device_info;
-  device_info.descriptor.device_id = device_id;
-  mojom::TextureVirtualDevicePtr device;
-  factory_->AddTextureVirtualDevice(device_info, mojo::MakeRequest(&device));
-  return device;
-}
-
-}  // namespace video_capture
diff --git a/services/video_capture/test/device_factory_provider_test.h b/services/video_capture/test/device_factory_provider_test.h
deleted file mode 100644
index 791fa80..0000000
--- a/services/video_capture/test/device_factory_provider_test.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_TEST_DEVICE_FACTORY_PROVIDER_TEST_H_
-#define SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_TEST_DEVICE_FACTORY_PROVIDER_TEST_H_
-
-#include "base/macros.h"
-#include "base/test/mock_callback.h"
-#include "base/test/scoped_task_environment.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/cpp/service.h"
-#include "services/service_manager/public/cpp/service_binding.h"
-#include "services/service_manager/public/cpp/test/test_service_manager.h"
-#include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
-#include "services/video_capture/public/mojom/virtual_device.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace video_capture {
-
-class MockProducer;
-
-// Basic test fixture that sets up a connection to the fake device factory.
-class DeviceFactoryProviderTest : public testing::Test {
- public:
-  DeviceFactoryProviderTest();
-  ~DeviceFactoryProviderTest() override;
-
-  void SetUp() override;
-
- protected:
-  struct SharedMemoryVirtualDeviceContext {
-    SharedMemoryVirtualDeviceContext(mojom::ProducerRequest producer_request);
-    ~SharedMemoryVirtualDeviceContext();
-
-    std::unique_ptr<MockProducer> mock_producer;
-    mojom::SharedMemoryVirtualDevicePtr device;
-  };
-
-  std::unique_ptr<SharedMemoryVirtualDeviceContext>
-  AddSharedMemoryVirtualDevice(const std::string& device_id);
-
-  mojom::TextureVirtualDevicePtr AddTextureVirtualDevice(
-      const std::string& device_id);
-
-  service_manager::Connector* connector() {
-    return test_service_binding_.GetConnector();
-  }
-
-  base::test::ScopedTaskEnvironment task_environment_;
-  service_manager::TestServiceManager test_service_manager_;
-  service_manager::Service test_service_;
-  service_manager::ServiceBinding test_service_binding_;
-
-  mojom::DeviceFactoryProviderPtr factory_provider_;
-  mojom::DeviceFactoryPtr factory_;
-  base::MockCallback<mojom::DeviceFactory::GetDeviceInfosCallback>
-      device_info_receiver_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceFactoryProviderTest);
-};
-
-}  // namespace video_capture
-
-#endif  // SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_TEST_DEVICE_FACTORY_PROVIDER_TEST_H_
diff --git a/services/video_capture/test/fake_device_descriptor_test.cc b/services/video_capture/test/fake_device_descriptor_test.cc
index 7156c06a..7f5a79b 100644
--- a/services/video_capture/test/fake_device_descriptor_test.cc
+++ b/services/video_capture/test/fake_device_descriptor_test.cc
@@ -12,12 +12,12 @@
 namespace video_capture {
 
 FakeDeviceDescriptorTest::FakeDeviceDescriptorTest()
-    : video_capture::DeviceFactoryProviderTest() {}
+    : VideoCaptureServiceTest() {}
 
 FakeDeviceDescriptorTest::~FakeDeviceDescriptorTest() = default;
 
 void FakeDeviceDescriptorTest::SetUp() {
-  video_capture::DeviceFactoryProviderTest::SetUp();
+  VideoCaptureServiceTest::SetUp();
 
   base::RunLoop wait_loop;
   EXPECT_CALL(device_info_receiver_, Run(_))
diff --git a/services/video_capture/test/fake_device_descriptor_test.h b/services/video_capture/test/fake_device_descriptor_test.h
index 069c77f..6219b4b 100644
--- a/services/video_capture/test/fake_device_descriptor_test.h
+++ b/services/video_capture/test/fake_device_descriptor_test.h
@@ -5,13 +5,13 @@
 #ifndef SERVICES_VIDEO_CAPTURE_TEST_FAKE_DEVICE_DESCRIPTOR_TEST_H_
 #define SERVICES_VIDEO_CAPTURE_TEST_FAKE_DEVICE_DESCRIPTOR_TEST_H_
 
-#include "services/video_capture/test/device_factory_provider_test.h"
+#include "services/video_capture/test/video_capture_service_test.h"
 
 namespace video_capture {
 
 // Test fixture that obtains the descriptor of the fake device by enumerating
 // the devices of the fake device factory.
-class FakeDeviceDescriptorTest : public DeviceFactoryProviderTest {
+class FakeDeviceDescriptorTest : public VideoCaptureServiceTest {
  public:
   FakeDeviceDescriptorTest();
   ~FakeDeviceDescriptorTest() override;
diff --git a/services/video_capture/test/mock_device_shared_access_unittest.cc b/services/video_capture/test/mock_device_shared_access_unittest.cc
index cfd1d4c..4a05a9c 100644
--- a/services/video_capture/test/mock_device_shared_access_unittest.cc
+++ b/services/video_capture/test/mock_device_shared_access_unittest.cc
@@ -12,11 +12,10 @@
 #include "media/capture/video/mock_device_factory.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video/video_capture_system_impl.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
 #include "services/video_capture/device_factory_media_to_mojo_adapter.h"
 #include "services/video_capture/device_media_to_mojo_adapter.h"
 #include "services/video_capture/public/cpp/mock_receiver.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 #include "services/video_capture/public/mojom/video_source.mojom.h"
 #include "services/video_capture/video_source_provider_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,8 +33,7 @@
   MockDeviceSharedAccessTest()
       : mock_receiver_1_(mojo::MakeRequest(&receiver_1_)),
         mock_receiver_2_(mojo::MakeRequest(&receiver_2_)),
-        next_arbitrary_frame_feedback_id_(123),
-        service_keepalive_(nullptr, base::nullopt) {}
+        next_arbitrary_frame_feedback_id_(123) {}
   ~MockDeviceSharedAccessTest() override {}
 
   void SetUp() override {
@@ -55,7 +53,6 @@
     service_device_factory_ = std::make_unique<DeviceFactoryMediaToMojoAdapter>(
         std::move(video_capture_system));
 #endif  // defined(OS_CHROMEOS)
-    service_device_factory_->SetServiceRef(service_keepalive_.CreateRef());
     source_provider_ = std::make_unique<VideoSourceProviderImpl>(
         service_device_factory_.get(), base::DoNothing());
 
@@ -268,7 +265,6 @@
   int32_t next_arbitrary_frame_feedback_id_;
 
  private:
-  service_manager::ServiceKeepalive service_keepalive_;
 };
 
 // This alias ensures test output is easily attributed to this service's tests.
diff --git a/services/video_capture/test/mock_device_test.cc b/services/video_capture/test/mock_device_test.cc
index 1f761ac..b18e7e7 100644
--- a/services/video_capture/test/mock_device_test.cc
+++ b/services/video_capture/test/mock_device_test.cc
@@ -15,7 +15,7 @@
 
 namespace video_capture {
 
-MockDeviceTest::MockDeviceTest() : service_keepalive_(nullptr, base::nullopt) {}
+MockDeviceTest::MockDeviceTest() = default;
 
 MockDeviceTest::~MockDeviceTest() = default;
 
@@ -38,7 +38,6 @@
       std::make_unique<DeviceFactoryMediaToMojoAdapter>(
           std::move(video_capture_system));
 #endif  // defined(OS_CHROMEOS)
-  mock_device_factory_adapter_->SetServiceRef(service_keepalive_.CreateRef());
 
   mock_factory_binding_ = std::make_unique<mojo::Binding<mojom::DeviceFactory>>(
       mock_device_factory_adapter_.get(), mojo::MakeRequest(&factory_));
diff --git a/services/video_capture/test/mock_device_test.h b/services/video_capture/test/mock_device_test.h
index 66055d83..d550b66 100644
--- a/services/video_capture/test/mock_device_test.h
+++ b/services/video_capture/test/mock_device_test.h
@@ -9,11 +9,10 @@
 #include "media/capture/video/mock_device.h"
 #include "media/capture/video/mock_device_factory.h"
 #include "media/capture/video/video_capture_device.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
 #include "services/video_capture/device_factory_media_to_mojo_adapter.h"
 #include "services/video_capture/public/cpp/mock_receiver.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
-#include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -47,7 +46,6 @@
 
  private:
   std::unique_ptr<base::MessageLoop> message_loop_;
-  service_manager::ServiceKeepalive service_keepalive_;
 };
 
 }  // namespace video_capture
diff --git a/services/video_capture/test/service_lifecycle_unittest.cc b/services/video_capture/test/service_lifecycle_unittest.cc
new file mode 100644
index 0000000..46c715e4
--- /dev/null
+++ b/services/video_capture/test/service_lifecycle_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/timer/timer.h"
+#include "media/base/media_switches.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/video_capture/public/cpp/mock_receiver.h"
+#include "services/video_capture/public/mojom/constants.mojom.h"
+#include "services/video_capture/public/mojom/device.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
+#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
+#include "services/video_capture/video_capture_service_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace video_capture {
+
+using testing::_;
+using testing::Exactly;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+
+// Test fixture that creates a video_capture::ServiceImpl and sets up a
+// local service_manager::Connector through which client code can connect to
+// it.
+class VideoCaptureServiceLifecycleTest : public ::testing::Test {
+ public:
+  VideoCaptureServiceLifecycleTest() = default;
+  ~VideoCaptureServiceLifecycleTest() override = default;
+
+  void SetUp() override {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kUseFakeDeviceForMediaStream);
+    service_impl_ = std::make_unique<VideoCaptureServiceImpl>(
+        service_remote_.BindNewPipeAndPassReceiver(),
+        scoped_task_environment_.GetMainThreadTaskRunner());
+    service_remote_.set_idle_handler(
+        base::TimeDelta(),
+        base::BindRepeating(&VideoCaptureServiceLifecycleTest::OnServiceIdle,
+                            base::Unretained(this)));
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<VideoCaptureServiceImpl> service_impl_;
+  mojo::Remote<mojom::VideoCaptureService> service_remote_;
+  base::MockCallback<mojom::DeviceFactory::GetDeviceInfosCallback>
+      device_info_receiver_;
+  base::RunLoop service_idle_wait_loop_;
+
+ private:
+  void OnServiceIdle() { service_idle_wait_loop_.Quit(); }
+
+  DISALLOW_COPY_AND_ASSIGN(VideoCaptureServiceLifecycleTest);
+};
+
+// Tests that the service quits when the only client disconnects after not
+// having done anything other than obtaining a connection to the device factory.
+TEST_F(VideoCaptureServiceLifecycleTest,
+       ServiceQuitsWhenSingleDeviceFactoryClientDisconnected) {
+  mojom::DeviceFactoryPtr factory;
+  service_remote_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
+  factory.reset();
+  service_idle_wait_loop_.Run();
+}
+
+// Tests that the service quits when the only client disconnects after not
+// having done anything other than obtaining a connection to the video source
+// provider.
+TEST_F(VideoCaptureServiceLifecycleTest,
+       ServiceQuitsWhenSingleVideoSourceProviderClientDisconnected) {
+  mojom::VideoSourceProviderPtr source_provider;
+  service_remote_->ConnectToVideoSourceProvider(
+      mojo::MakeRequest(&source_provider));
+  source_provider.reset();
+  service_idle_wait_loop_.Run();
+}
+
+// Tests that the service quits when the only client disconnects after
+// enumerating devices via the video source provider.
+TEST_F(VideoCaptureServiceLifecycleTest, ServiceQuitsAfterEnumeratingDevices) {
+  mojom::VideoSourceProviderPtr source_provider;
+  service_remote_->ConnectToVideoSourceProvider(
+      mojo::MakeRequest(&source_provider));
+
+  base::RunLoop wait_loop;
+  EXPECT_CALL(device_info_receiver_, Run(_))
+      .WillOnce(
+          Invoke([&wait_loop](
+                     const std::vector<media::VideoCaptureDeviceInfo>& infos) {
+            wait_loop.Quit();
+          }));
+  source_provider->GetSourceInfos(device_info_receiver_.Get());
+  wait_loop.Run();
+
+  source_provider.reset();
+
+  service_idle_wait_loop_.Run();
+}
+
+// Tests that enumerating devices works after the only client disconnects and
+// reconnects via the video source provider.
+TEST_F(VideoCaptureServiceLifecycleTest, EnumerateDevicesAfterReconnect) {
+  // Connect |source_provider|.
+  mojom::VideoSourceProviderPtr source_provider;
+  service_remote_->ConnectToVideoSourceProvider(
+      mojo::MakeRequest(&source_provider));
+
+  // Disconnect |source_provider| and wait for the disconnect to propagate to
+  // the service.
+  {
+    base::RunLoop wait_loop;
+    source_provider->Close(base::BindOnce(
+        [](base::RunLoop* wait_loop) { wait_loop->Quit(); }, &wait_loop));
+    wait_loop.Run();
+    source_provider.reset();
+  }
+
+  // Reconnect |source_provider|.
+  service_remote_->ConnectToVideoSourceProvider(
+      mojo::MakeRequest(&source_provider));
+
+  // Enumerate devices.
+  base::RunLoop wait_loop;
+  EXPECT_CALL(device_info_receiver_, Run(_))
+      .WillOnce(
+          Invoke([&wait_loop](
+                     const std::vector<media::VideoCaptureDeviceInfo>& infos) {
+            wait_loop.Quit();
+          }));
+  source_provider->GetSourceInfos(device_info_receiver_.Get());
+  wait_loop.Run();
+
+  source_provider.reset();
+
+  service_idle_wait_loop_.Run();
+}
+
+// Tests that the service quits when the last client disconnects while using a
+// device.
+TEST_F(VideoCaptureServiceLifecycleTest,
+       ServiceQuitsWhenClientDisconnectsWhileUsingDevice) {
+  mojom::DeviceFactoryPtr factory;
+  service_remote_->ConnectToDeviceFactory(mojo::MakeRequest(&factory));
+
+  // Connect to and start first device (in this case a fake camera).
+  media::VideoCaptureDeviceInfo fake_device_info;
+  {
+    base::RunLoop wait_loop;
+    EXPECT_CALL(device_info_receiver_, Run(_))
+        .WillOnce(Invoke(
+            [&fake_device_info, &wait_loop](
+                const std::vector<media::VideoCaptureDeviceInfo>& infos) {
+              fake_device_info = infos[0];
+              wait_loop.Quit();
+            }));
+    factory->GetDeviceInfos(device_info_receiver_.Get());
+    wait_loop.Run();
+  }
+  mojom::DevicePtr fake_device;
+  factory->CreateDevice(
+      std::move(fake_device_info.descriptor.device_id),
+      mojo::MakeRequest(&fake_device),
+      base::BindOnce([](mojom::DeviceAccessResultCode result_code) {
+        ASSERT_EQ(mojom::DeviceAccessResultCode::SUCCESS, result_code);
+      }));
+  media::VideoCaptureParams requestable_settings;
+  requestable_settings.requested_format = fake_device_info.supported_formats[0];
+  mojom::ReceiverPtr receiver_proxy;
+  MockReceiver mock_receiver(mojo::MakeRequest(&receiver_proxy));
+  fake_device->Start(requestable_settings, std::move(receiver_proxy));
+  {
+    base::RunLoop wait_loop;
+    EXPECT_CALL(mock_receiver, OnStarted()).WillOnce([&wait_loop]() {
+      wait_loop.Quit();
+    });
+    wait_loop.Run();
+  }
+
+  // Disconnect
+  fake_device.reset();
+  factory.reset();
+
+  service_idle_wait_loop_.Run();
+}
+
+}  // namespace video_capture
diff --git a/services/video_capture/test/video_capture_service_test.cc b/services/video_capture/test/video_capture_service_test.cc
new file mode 100644
index 0000000..7d06979d
--- /dev/null
+++ b/services/video_capture/test/video_capture_service_test.cc
@@ -0,0 +1,68 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/video_capture/test/video_capture_service_test.h"
+
+#include "base/command_line.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/base/media_switches.h"
+#include "services/video_capture/public/cpp/mock_producer.h"
+#include "services/video_capture/public/mojom/constants.mojom.h"
+
+namespace video_capture {
+
+VideoCaptureServiceTest::SharedMemoryVirtualDeviceContext::
+    SharedMemoryVirtualDeviceContext(mojom::ProducerRequest producer_request)
+    : mock_producer(
+          std::make_unique<MockProducer>(std::move(producer_request))) {}
+
+VideoCaptureServiceTest::SharedMemoryVirtualDeviceContext::
+    ~SharedMemoryVirtualDeviceContext() = default;
+
+VideoCaptureServiceTest::VideoCaptureServiceTest() = default;
+
+VideoCaptureServiceTest::~VideoCaptureServiceTest() = default;
+
+void VideoCaptureServiceTest::SetUp() {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kUseFakeMjpegDecodeAccelerator);
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kUseFakeDeviceForMediaStream, "device-count=3");
+
+  service_impl_ = std::make_unique<VideoCaptureServiceImpl>(
+      service_remote_.BindNewPipeAndPassReceiver(),
+      base::ThreadTaskRunnerHandle::Get());
+
+  // Note, that we explicitly do *not* call
+  // |service_remote_->InjectGpuDependencies()| here. Test case
+  // |FakeMjpegVideoCaptureDeviceTest.
+  //  CanDecodeMjpegWithoutInjectedGpuDependencies| depends on this assumption.
+  service_remote_->ConnectToDeviceFactory(mojo::MakeRequest(&factory_));
+}
+
+std::unique_ptr<VideoCaptureServiceTest::SharedMemoryVirtualDeviceContext>
+VideoCaptureServiceTest::AddSharedMemoryVirtualDevice(
+    const std::string& device_id) {
+  media::VideoCaptureDeviceInfo device_info;
+  device_info.descriptor.device_id = device_id;
+  mojom::ProducerPtr producer;
+  auto result = std::make_unique<SharedMemoryVirtualDeviceContext>(
+      mojo::MakeRequest(&producer));
+  factory_->AddSharedMemoryVirtualDevice(
+      device_info, std::move(producer),
+      false /* send_buffer_handles_to_producer_as_raw_file_descriptors */,
+      mojo::MakeRequest(&result->device));
+  return result;
+}
+
+mojom::TextureVirtualDevicePtr VideoCaptureServiceTest::AddTextureVirtualDevice(
+    const std::string& device_id) {
+  media::VideoCaptureDeviceInfo device_info;
+  device_info.descriptor.device_id = device_id;
+  mojom::TextureVirtualDevicePtr device;
+  factory_->AddTextureVirtualDevice(device_info, mojo::MakeRequest(&device));
+  return device;
+}
+
+}  // namespace video_capture
diff --git a/services/video_capture/test/video_capture_service_test.h b/services/video_capture/test/video_capture_service_test.h
new file mode 100644
index 0000000..e641674
--- /dev/null
+++ b/services/video_capture/test/video_capture_service_test.h
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_VIDEO_CAPTURE_TEST_VIDEO_CAPTURE_SERVICE_TEST_H_
+#define SERVICES_VIDEO_CAPTURE_TEST_VIDEO_CAPTURE_SERVICE_TEST_H_
+
+#include "base/macros.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/video_capture/public/mojom/device_factory.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
+#include "services/video_capture/public/mojom/virtual_device.mojom.h"
+#include "services/video_capture/video_capture_service_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace video_capture {
+
+class MockProducer;
+
+// Basic test fixture that sets up a connection to the fake device factory.
+class VideoCaptureServiceTest : public testing::Test {
+ public:
+  VideoCaptureServiceTest();
+  ~VideoCaptureServiceTest() override;
+
+  void SetUp() override;
+
+ protected:
+  struct SharedMemoryVirtualDeviceContext {
+    SharedMemoryVirtualDeviceContext(mojom::ProducerRequest producer_request);
+    ~SharedMemoryVirtualDeviceContext();
+
+    std::unique_ptr<MockProducer> mock_producer;
+    mojom::SharedMemoryVirtualDevicePtr device;
+  };
+
+  std::unique_ptr<SharedMemoryVirtualDeviceContext>
+  AddSharedMemoryVirtualDevice(const std::string& device_id);
+
+  mojom::TextureVirtualDevicePtr AddTextureVirtualDevice(
+      const std::string& device_id);
+
+  base::test::ScopedTaskEnvironment task_environment_;
+
+  std::unique_ptr<VideoCaptureServiceImpl> service_impl_;
+  mojo::Remote<mojom::VideoCaptureService> service_remote_;
+  mojom::DeviceFactoryPtr factory_;
+  base::MockCallback<mojom::DeviceFactory::GetDeviceInfosCallback>
+      device_info_receiver_;
+
+  DISALLOW_COPY_AND_ASSIGN(VideoCaptureServiceTest);
+};
+
+}  // namespace video_capture
+
+#endif  // SERVICES_VIDEO_CAPTURE_TEST_VIDEO_CAPTURE_SERVICE_TEST_H_
diff --git a/services/video_capture/test/device_factory_provider_unittest.cc b/services/video_capture/test/video_capture_service_unittest.cc
similarity index 87%
rename from services/video_capture/test/device_factory_provider_unittest.cc
rename to services/video_capture/test/video_capture_service_unittest.cc
index a68b27a..91c4160 100644
--- a/services/video_capture/test/device_factory_provider_unittest.cc
+++ b/services/video_capture/test/video_capture_service_unittest.cc
@@ -11,8 +11,8 @@
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
 #include "services/video_capture/public/mojom/virtual_device.mojom.h"
-#include "services/video_capture/test/device_factory_provider_test.h"
 #include "services/video_capture/test/mock_devices_changed_observer.h"
+#include "services/video_capture/test/video_capture_service_test.h"
 
 using testing::_;
 using testing::Exactly;
@@ -21,14 +21,9 @@
 
 namespace video_capture {
 
-// This alias ensures test output is easily attributed to this service's tests.
-// TODO(rockot/chfremer): Consider just renaming the type.
-using VideoCaptureServiceDeviceFactoryProviderTest = DeviceFactoryProviderTest;
-
 // Tests that an answer arrives from the service when calling
 // GetDeviceInfos().
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
-       GetDeviceInfosCallbackArrives) {
+TEST_F(VideoCaptureServiceTest, GetDeviceInfosCallbackArrives) {
   base::RunLoop wait_loop;
   EXPECT_CALL(device_info_receiver_, Run(_))
       .Times(Exactly(1))
@@ -38,8 +33,7 @@
   wait_loop.Run();
 }
 
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
-       FakeDeviceFactoryEnumeratesThreeDevices) {
+TEST_F(VideoCaptureServiceTest, FakeDeviceFactoryEnumeratesThreeDevices) {
   base::RunLoop wait_loop;
   size_t num_devices_enumerated = 0;
   EXPECT_CALL(device_info_receiver_, Run(_))
@@ -58,8 +52,7 @@
 
 // Tests that an added virtual device will be returned in the callback
 // when calling GetDeviceInfos.
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
-       VirtualDeviceEnumeratedAfterAdd) {
+TEST_F(VideoCaptureServiceTest, VirtualDeviceEnumeratedAfterAdd) {
   const std::string virtual_device_id = "/virtual/device";
   auto device_context = AddSharedMemoryVirtualDevice(virtual_device_id);
 
@@ -83,7 +76,7 @@
   wait_loop.Run();
 }
 
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
+TEST_F(VideoCaptureServiceTest,
        AddingAndRemovingVirtualDevicesRaisesDevicesChangedEvent) {
   mojom::DevicesChangedObserverPtr observer;
   MockDevicesChangedObserver mock_observer;
@@ -130,7 +123,7 @@
 
 // Tests that disconnecting a devices changed observer does not lead to any
 // crash or bad state.
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
+TEST_F(VideoCaptureServiceTest,
        AddAndRemoveVirtualDeviceAfterObserverHasDisconnected) {
   mojom::DevicesChangedObserverPtr observer;
   MockDevicesChangedObserver mock_observer;
@@ -149,8 +142,7 @@
 
 // Tests that VideoCaptureDeviceFactory::CreateDevice() returns an error
 // code when trying to create a device for an invalid descriptor.
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
-       ErrorCodeOnCreateDeviceForInvalidDescriptor) {
+TEST_F(VideoCaptureServiceTest, ErrorCodeOnCreateDeviceForInvalidDescriptor) {
   const std::string invalid_device_id = "invalid";
   base::RunLoop wait_loop;
   mojom::DevicePtr fake_device_proxy;
@@ -169,8 +161,7 @@
 
 // Test that CreateDevice() will succeed when trying to create a device
 // for an added virtual device.
-TEST_F(VideoCaptureServiceDeviceFactoryProviderTest,
-       CreateDeviceSuccessForVirtualDevice) {
+TEST_F(VideoCaptureServiceTest, CreateDeviceSuccessForVirtualDevice) {
   base::RunLoop wait_loop;
   const std::string virtual_device_id = "/virtual/device";
   auto device_context = AddSharedMemoryVirtualDevice(virtual_device_id);
diff --git a/services/video_capture/test/virtual_device_unittest.cc b/services/video_capture/test/virtual_device_unittest.cc
index df0682b..0b82327 100644
--- a/services/video_capture/test/virtual_device_unittest.cc
+++ b/services/video_capture/test/virtual_device_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "media/capture/video/video_capture_device_info.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
 #include "services/video_capture/public/cpp/mock_producer.h"
 #include "services/video_capture/public/cpp/mock_receiver.h"
 #include "services/video_capture/shared_memory_virtual_device_mojo_adapter.h"
@@ -32,8 +31,8 @@
 
 class VirtualDeviceTest : public ::testing::Test {
  public:
-  VirtualDeviceTest() : keepalive_(nullptr, base::nullopt) {}
-  ~VirtualDeviceTest() override {}
+  VirtualDeviceTest() = default;
+  ~VirtualDeviceTest() override = default;
 
   void SetUp() override {
     device_info_.descriptor.device_id = kTestDeviceId;
@@ -42,7 +41,7 @@
     producer_ =
         std::make_unique<MockProducer>(mojo::MakeRequest(&producer_proxy));
     device_adapter_ = std::make_unique<SharedMemoryVirtualDeviceMojoAdapter>(
-        keepalive_.CreateRef(), std::move(producer_proxy));
+        std::move(producer_proxy));
   }
 
   void OnFrameBufferReceived(bool valid_buffer_expected, int32_t buffer_id) {
@@ -97,7 +96,6 @@
 
  private:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
-  service_manager::ServiceKeepalive keepalive_;
   media::VideoCaptureDeviceInfo device_info_;
 };
 
diff --git a/services/video_capture/testing_controls_impl.cc b/services/video_capture/testing_controls_impl.cc
index e89eb86..3dc4e69af 100644
--- a/services/video_capture/testing_controls_impl.cc
+++ b/services/video_capture/testing_controls_impl.cc
@@ -4,11 +4,11 @@
 
 #include "services/video_capture/testing_controls_impl.h"
 
+#include "base/logging.h"
+
 namespace video_capture {
 
-TestingControlsImpl::TestingControlsImpl(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+TestingControlsImpl::TestingControlsImpl() = default;
 
 TestingControlsImpl::~TestingControlsImpl() = default;
 
diff --git a/services/video_capture/testing_controls_impl.h b/services/video_capture/testing_controls_impl.h
index 860820b..eb051d1 100644
--- a/services/video_capture/testing_controls_impl.h
+++ b/services/video_capture/testing_controls_impl.h
@@ -5,23 +5,19 @@
 #ifndef SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
 #define SERVICES_VIDEO_CAPTURE_TESTING_CONTROLS_IMPL_H_
 
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/testing_controls.mojom.h"
 
 namespace video_capture {
 
 class TestingControlsImpl : public mojom::TestingControls {
  public:
-  TestingControlsImpl(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  TestingControlsImpl();
   ~TestingControlsImpl() override;
 
   // mojom::TestingControls implementation.
   void Crash() override;
 
  private:
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-
   DISALLOW_COPY_AND_ASSIGN(TestingControlsImpl);
 };
 
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter.cc b/services/video_capture/texture_virtual_device_mojo_adapter.cc
index 42c604de..3239a4d 100644
--- a/services/video_capture/texture_virtual_device_mojo_adapter.cc
+++ b/services/video_capture/texture_virtual_device_mojo_adapter.cc
@@ -15,9 +15,7 @@
 
 namespace video_capture {
 
-TextureVirtualDeviceMojoAdapter::TextureVirtualDeviceMojoAdapter(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
-    : service_ref_(std::move(service_ref)) {}
+TextureVirtualDeviceMojoAdapter::TextureVirtualDeviceMojoAdapter() = default;
 
 TextureVirtualDeviceMojoAdapter::~TextureVirtualDeviceMojoAdapter() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter.h b/services/video_capture/texture_virtual_device_mojo_adapter.h
index 8f1749e..2293ae0 100644
--- a/services/video_capture/texture_virtual_device_mojo_adapter.h
+++ b/services/video_capture/texture_virtual_device_mojo_adapter.h
@@ -8,7 +8,6 @@
 #include "base/sequence_checker.h"
 #include "media/capture/video/video_capture_buffer_pool.h"
 #include "media/capture/video_capture_types.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/producer.mojom.h"
 #include "services/video_capture/public/mojom/receiver.mojom.h"
@@ -19,8 +18,7 @@
 class TextureVirtualDeviceMojoAdapter : public mojom::TextureVirtualDevice,
                                         public mojom::Device {
  public:
-  explicit TextureVirtualDeviceMojoAdapter(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  TextureVirtualDeviceMojoAdapter();
   ~TextureVirtualDeviceMojoAdapter() override;
 
   void SetReceiverDisconnectedCallback(base::OnceClosure callback);
@@ -50,7 +48,6 @@
  private:
   void OnReceiverConnectionErrorOrClose();
 
-  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   base::OnceClosure optional_receiver_disconnected_callback_;
   mojom::ReceiverPtr receiver_;
   std::unordered_map<int32_t, media::mojom::MailboxBufferHandleSetPtr>
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc b/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc
index 4ec7f3bc..f0774b7 100644
--- a/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc
+++ b/services/video_capture/texture_virtual_device_mojo_adapter_unittest.cc
@@ -17,16 +17,14 @@
 
 class TextureVirtualDeviceMojoAdapterTest : public ::testing::Test {
  public:
-  TextureVirtualDeviceMojoAdapterTest()
-      : service_keepalive_(nullptr, base::nullopt) {}
+  TextureVirtualDeviceMojoAdapterTest() = default;
 
   void SetUp() override {
     mock_receiver_1_ =
         std::make_unique<MockReceiver>(mojo::MakeRequest(&receiver_1_));
     mock_receiver_2_ =
         std::make_unique<MockReceiver>(mojo::MakeRequest(&receiver_2_));
-    adapter_ = std::make_unique<TextureVirtualDeviceMojoAdapter>(
-        service_keepalive_.CreateRef());
+    adapter_ = std::make_unique<TextureVirtualDeviceMojoAdapter>();
   }
 
  protected:
@@ -63,7 +61,6 @@
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
-  service_manager::ServiceKeepalive service_keepalive_;
   std::unique_ptr<TextureVirtualDeviceMojoAdapter> adapter_;
   mojom::ReceiverPtr receiver_1_;
   mojom::ReceiverPtr receiver_2_;
diff --git a/services/video_capture/device_factory_provider_impl.cc b/services/video_capture/video_capture_service_impl.cc
similarity index 66%
rename from services/video_capture/device_factory_provider_impl.cc
rename to services/video_capture/video_capture_service_impl.cc
index 154a045..b6a7238 100644
--- a/services/video_capture/device_factory_provider_impl.cc
+++ b/services/video_capture/video_capture_service_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/video_capture/device_factory_provider_impl.h"
+#include "services/video_capture/video_capture_service_impl.h"
 
 #include <utility>
 
@@ -15,7 +15,9 @@
 #include "media/capture/video/video_capture_buffer_pool.h"
 #include "media/capture/video/video_capture_buffer_tracker.h"
 #include "media/capture/video/video_capture_system_impl.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "services/video_capture/device_factory_media_to_mojo_adapter.h"
+#include "services/video_capture/testing_controls_impl.h"
 #include "services/video_capture/video_source_provider_impl.h"
 #include "services/video_capture/virtual_device_enabled_device_factory.h"
 #include "services/viz/public/cpp/gpu/gpu.h"
@@ -31,7 +33,7 @@
 // GetTaskRunner() via WeakPtrs provided via GetWeakPtr(). To this end,
 // GetTaskRunner() and GetWeakPtr() can be called from any sequence, typically
 // the same as the one calling the constructor.
-class DeviceFactoryProviderImpl::GpuDependenciesContext {
+class VideoCaptureServiceImpl::GpuDependenciesContext {
  public:
   GpuDependenciesContext() : weak_factory_for_gpu_io_thread_(this) {
     gpu_io_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
@@ -82,19 +84,13 @@
   base::WeakPtrFactory<GpuDependenciesContext> weak_factory_for_gpu_io_thread_;
 };
 
-DeviceFactoryProviderImpl::DeviceFactoryProviderImpl(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-    base::OnceClosure request_service_quit_asap_cb)
-    : ui_task_runner_(std::move(ui_task_runner)),
-      request_service_quit_asap_cb_(std::move(request_service_quit_asap_cb)) {
-  // Unretained |this| is safe because |factory_bindings_| is owned by
-  // |this|.
-  factory_bindings_.set_connection_error_handler(base::BindRepeating(
-      &DeviceFactoryProviderImpl::OnFactoryOrSourceProviderClientDisconnected,
-      base::Unretained(this)));
-}
+VideoCaptureServiceImpl::VideoCaptureServiceImpl(
+    mojo::PendingReceiver<mojom::VideoCaptureService> receiver,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+    : receiver_(this, std::move(receiver)),
+      ui_task_runner_(std::move(ui_task_runner)) {}
 
-DeviceFactoryProviderImpl::~DeviceFactoryProviderImpl() {
+VideoCaptureServiceImpl::~VideoCaptureServiceImpl() {
   factory_bindings_.CloseAllBindings();
   device_factory_.reset();
   if (gpu_dependencies_context_) {
@@ -103,13 +99,8 @@
   }
 }
 
-void DeviceFactoryProviderImpl::SetServiceRef(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref) {
-  service_ref_ = std::move(service_ref);
-}
-
 #if defined(OS_CHROMEOS)
-void DeviceFactoryProviderImpl::InjectGpuDependencies(
+void VideoCaptureServiceImpl::InjectGpuDependencies(
     mojom::AcceleratorFactoryPtr accelerator_factory) {
   LazyInitializeGpuDependenciesContext();
   gpu_dependencies_context_->GetTaskRunner()->PostTask(
@@ -117,47 +108,46 @@
                                 gpu_dependencies_context_->GetWeakPtr(),
                                 accelerator_factory.PassInterface()));
 }
+
+void VideoCaptureServiceImpl::BindCrosImageCapture(
+    mojo::PendingReceiver<cros::mojom::CrosImageCapture> receiver) {
+  CHECK(device_factory_);
+  device_factory_->BindCrosImageCaptureRequest(std::move(receiver));
+}
 #endif  // defined(OS_CHROMEOS)
 
-void DeviceFactoryProviderImpl::ConnectToDeviceFactory(
+void VideoCaptureServiceImpl::ConnectToDeviceFactory(
     mojom::DeviceFactoryRequest request) {
-  DCHECK(service_ref_);
   LazyInitializeDeviceFactory();
   factory_bindings_.AddBinding(device_factory_.get(), std::move(request));
 }
 
-void DeviceFactoryProviderImpl::ConnectToVideoSourceProvider(
+void VideoCaptureServiceImpl::ConnectToVideoSourceProvider(
     mojom::VideoSourceProviderRequest request) {
   LazyInitializeVideoSourceProvider();
   video_source_provider_->AddClient(std::move(request));
 }
 
-void DeviceFactoryProviderImpl::ShutdownServiceAsap() {
-  if (request_service_quit_asap_cb_)
-    std::move(request_service_quit_asap_cb_).Run();
-}
-
-void DeviceFactoryProviderImpl::SetRetryCount(int32_t count) {
+void VideoCaptureServiceImpl::SetRetryCount(int32_t count) {
 #if defined(OS_MACOSX)
   media::VideoCaptureDeviceFactoryMac::SetGetDeviceDescriptorsRetryCount(count);
 #endif
 }
 
-void DeviceFactoryProviderImpl::LazyInitializeGpuDependenciesContext() {
+void VideoCaptureServiceImpl::BindControlsForTesting(
+    mojo::PendingReceiver<mojom::TestingControls> receiver) {
+  mojo::MakeSelfOwnedReceiver(std::make_unique<TestingControlsImpl>(),
+                              std::move(receiver));
+}
+
+void VideoCaptureServiceImpl::LazyInitializeGpuDependenciesContext() {
   if (!gpu_dependencies_context_)
     gpu_dependencies_context_ = std::make_unique<GpuDependenciesContext>();
 }
 
-void DeviceFactoryProviderImpl::LazyInitializeDeviceFactory() {
-  DCHECK(service_ref_);
-
-  // Factory may already exist but if no client was connected it will not have a
-  // ServiceRef.
-  if (device_factory_) {
-    if (factory_bindings_.empty())
-      device_factory_->SetServiceRef(service_ref_->Clone());
+void VideoCaptureServiceImpl::LazyInitializeDeviceFactory() {
+  if (device_factory_)
     return;
-  }
 
   LazyInitializeGpuDependenciesContext();
 
@@ -185,11 +175,9 @@
       std::make_unique<DeviceFactoryMediaToMojoAdapter>(
           std::move(video_capture_system)));
 #endif  // defined(OS_CHROMEOS)
-
-  device_factory_->SetServiceRef(service_ref_->Clone());
 }
 
-void DeviceFactoryProviderImpl::LazyInitializeVideoSourceProvider() {
+void VideoCaptureServiceImpl::LazyInitializeVideoSourceProvider() {
   if (video_source_provider_)
     return;
   LazyInitializeDeviceFactory();
@@ -197,39 +185,12 @@
       device_factory_.get(),
       // Unretained(this) is safe, because |this| owns |video_source_provider_|.
       base::BindRepeating(
-          &DeviceFactoryProviderImpl::OnLastSourceProviderClientDisconnected,
+          &VideoCaptureServiceImpl::OnLastSourceProviderClientDisconnected,
           base::Unretained(this)));
 }
 
-void DeviceFactoryProviderImpl::OnLastSourceProviderClientDisconnected() {
+void VideoCaptureServiceImpl::OnLastSourceProviderClientDisconnected() {
   video_source_provider_.reset();
-  OnFactoryOrSourceProviderClientDisconnected();
 }
 
-void DeviceFactoryProviderImpl::OnFactoryOrSourceProviderClientDisconnected() {
-  // If |video_source_provider_| still exists, it means there is still a client
-  // connected to it, in which case we also still need |device_factory_| to
-  // stay operational.
-  if (video_source_provider_)
-    return;
-
-  // If neither |device_factory_| nor |video_source_provider_| have clients
-  // connected, release service ref so that service shutdown timeout can start
-  // if no other references are still alive. We keep the |device_factory_|
-  // instance alive in order to avoid losing state that would be expensive to
-  // reinitialize, e.g. having already enumerated the available devices.
-  if (factory_bindings_.empty()) {
-    device_factory_->SetServiceRef(nullptr);
-  }
-}
-
-#if defined(OS_CHROMEOS)
-void DeviceFactoryProviderImpl::BindCrosImageCaptureRequest(
-    cros::mojom::CrosImageCaptureRequest request) {
-  CHECK(device_factory_);
-
-  device_factory_->BindCrosImageCaptureRequest(std::move(request));
-}
-#endif  // defined(OS_CHROMEOS)
-
 }  // namespace video_capture
diff --git a/services/video_capture/video_capture_service_impl.h b/services/video_capture/video_capture_service_impl.h
new file mode 100644
index 0000000..e98751c
--- /dev/null
+++ b/services/video_capture/video_capture_service_impl.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_SERVICE_IMPL_H_
+#define SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_SERVICE_IMPL_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/threading/thread.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "services/video_capture/public/mojom/device_factory.mojom.h"
+#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
+
+#if defined(OS_CHROMEOS)
+#include "media/capture/video/chromeos/mojo/cros_image_capture.mojom.h"
+#endif  // defined(OS_CHROMEOS)
+
+namespace video_capture {
+
+class VirtualDeviceEnabledDeviceFactory;
+class VideoSourceProviderImpl;
+
+class VideoCaptureServiceImpl : public mojom::VideoCaptureService {
+ public:
+  explicit VideoCaptureServiceImpl(
+      mojo::PendingReceiver<mojom::VideoCaptureService> receiver,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+  ~VideoCaptureServiceImpl() override;
+
+  // mojom::VideoCaptureService implementation.
+#if defined(OS_CHROMEOS)
+  void InjectGpuDependencies(
+      mojom::AcceleratorFactoryPtr accelerator_factory) override;
+  void BindCrosImageCapture(
+      mojo::PendingReceiver<cros::mojom::CrosImageCapture> receiver) override;
+#endif  // defined(OS_CHROMEOS)
+  void ConnectToDeviceFactory(mojom::DeviceFactoryRequest request) override;
+  void ConnectToVideoSourceProvider(
+      mojom::VideoSourceProviderRequest request) override;
+  void SetRetryCount(int32_t count) override;
+  void BindControlsForTesting(
+      mojo::PendingReceiver<mojom::TestingControls> receiver) override;
+
+ private:
+  class GpuDependenciesContext;
+
+  void LazyInitializeGpuDependenciesContext();
+  void LazyInitializeDeviceFactory();
+  void LazyInitializeVideoSourceProvider();
+  void OnLastSourceProviderClientDisconnected();
+
+  mojo::Receiver<mojom::VideoCaptureService> receiver_;
+  mojo::BindingSet<mojom::DeviceFactory> factory_bindings_;
+  std::unique_ptr<VirtualDeviceEnabledDeviceFactory> device_factory_;
+  std::unique_ptr<VideoSourceProviderImpl> video_source_provider_;
+  std::unique_ptr<GpuDependenciesContext> gpu_dependencies_context_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(VideoCaptureServiceImpl);
+};
+
+}  // namespace video_capture
+
+#endif  // SERVICES_VIDEO_CAPTURE_VIDEO_CAPTURE_SERVICE_IMPL_H_
diff --git a/services/video_capture/video_source_provider_impl.h b/services/video_capture/video_source_provider_impl.h
index 468ae6d..f7d0ecd 100644
--- a/services/video_capture/video_source_provider_impl.h
+++ b/services/video_capture/video_source_provider_impl.h
@@ -51,7 +51,6 @@
   int client_count_ = 0;
   int closed_but_not_yet_disconnected_client_count_ = 0;
   mojo::BindingSet<mojom::VideoSourceProvider> bindings_;
-  std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   std::map<std::string, std::unique_ptr<VideoSourceImpl>> sources_;
   DISALLOW_COPY_AND_ASSIGN(VideoSourceProviderImpl);
 };
diff --git a/services/video_capture/virtual_device_enabled_device_factory.cc b/services/video_capture/virtual_device_enabled_device_factory.cc
index 6db39c7..cd41361 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.cc
+++ b/services/video_capture/virtual_device_enabled_device_factory.cc
@@ -92,15 +92,6 @@
 VirtualDeviceEnabledDeviceFactory::~VirtualDeviceEnabledDeviceFactory() =
     default;
 
-void VirtualDeviceEnabledDeviceFactory::SetServiceRef(
-    std::unique_ptr<service_manager::ServiceContextRef> service_ref) {
-  if (service_ref)
-    device_factory_->SetServiceRef(service_ref->Clone());
-  else
-    device_factory_->SetServiceRef(nullptr);
-  service_ref_ = std::move(service_ref);
-}
-
 void VirtualDeviceEnabledDeviceFactory::GetDeviceInfos(
     GetDeviceInfosCallback callback) {
   device_factory_->GetDeviceInfos(
@@ -152,7 +143,7 @@
                      OnVirtualDeviceProducerConnectionErrorOrClose,
                  base::Unretained(this), device_id));
   auto device = std::make_unique<SharedMemoryVirtualDeviceMojoAdapter>(
-      service_ref_->Clone(), std::move(producer),
+      std::move(producer),
       send_buffer_handles_to_producer_as_raw_file_descriptors);
   auto producer_binding =
       std::make_unique<mojo::Binding<mojom::SharedMemoryVirtualDevice>>(
@@ -179,8 +170,7 @@
     virtual_devices_by_id_.erase(virtual_device_iter);
   }
 
-  auto device =
-      std::make_unique<TextureVirtualDeviceMojoAdapter>(service_ref_->Clone());
+  auto device = std::make_unique<TextureVirtualDeviceMojoAdapter>();
   auto producer_binding =
       std::make_unique<mojo::Binding<mojom::TextureVirtualDevice>>(
           device.get(), std::move(virtual_device_request));
diff --git a/services/video_capture/virtual_device_enabled_device_factory.h b/services/video_capture/virtual_device_enabled_device_factory.h
index 0d74f64..8c812c2 100644
--- a/services/video_capture/virtual_device_enabled_device_factory.h
+++ b/services/video_capture/virtual_device_enabled_device_factory.h
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/device_factory.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 #include "services/video_capture/public/mojom/devices_changed_observer.mojom.h"
@@ -29,8 +28,6 @@
   ~VirtualDeviceEnabledDeviceFactory() override;
 
   // DeviceFactory implementation.
-  void SetServiceRef(
-      std::unique_ptr<service_manager::ServiceContextRef> service_ref) override;
   void GetDeviceInfos(GetDeviceInfosCallback callback) override;
   void CreateDevice(const std::string& device_id,
                     mojom::DeviceRequest device_request,
@@ -69,7 +66,6 @@
 
   std::map<std::string, VirtualDeviceEntry> virtual_devices_by_id_;
   const std::unique_ptr<DeviceFactory> device_factory_;
-  std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   std::vector<mojom::DevicesChangedObserverPtr> devices_changed_observers_;
 
   base::WeakPtrFactory<VirtualDeviceEnabledDeviceFactory> weak_factory_;
diff --git a/testing/buildbot/chrome.json b/testing/buildbot/chrome.json
index b91b9b0..2b5d7bd 100644
--- a/testing/buildbot/chrome.json
+++ b/testing/buildbot/chrome.json
@@ -318,7 +318,8 @@
       },
       {
         "args": [
-          "--vpython-dir=../../vpython_dir_linux_amd64"
+          "--vpython-dir=../../vpython_dir_linux_amd64",
+          "--gtest_filter=-NetworkQualityEstimatorTest.TestTCPSocketRTT"
         ],
         "merge": {
           "args": [],
diff --git a/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter b/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter
index 044935e..5d006fa 100644
--- a/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter
+++ b/testing/buildbot/filters/navigation_loader_on_ui_browser_tests.filter
@@ -1,4 +1 @@
 # These tests currently fail when run with --enable-features=NavigationLoaderOnUI
-
-# https://crbug.com/978617
--GcmApiTest.*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 43d19f8..6ad7a86 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -899,6 +899,12 @@
           'shards': 2,
         },
       },
+      'chromeos-betty-google-rel': {
+        # TODO(crbug.com/986904): Remove this filter.
+        'args': [
+          '--gtest_filter=-NetworkQualityEstimatorTest.TestTCPSocketRTT',
+        ],
+      },
       'linux-chromeos-dbg': {
         'swarming': {
           'shards': 2,
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
index 4297786..c16d7e1 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
@@ -75,7 +75,7 @@
       const ComputedStyle&,
       const SVGComputedStyle&,
       const LayoutObject*,
-      Node*,
+      const Node*,
       bool allow_visited_style) const override {
     // Directional properties are resolved by CSSDirectionAwareResolver
     // before calling CSSValueFromComputedStyleInternal.
diff --git a/third_party/blink/renderer/core/animation/animation_effect.cc b/third_party/blink/renderer/core/animation/animation_effect.cc
index cef9fcf8..a27cf06e 100644
--- a/third_party/blink/renderer/core/animation/animation_effect.cc
+++ b/third_party/blink/renderer/core/animation/animation_effect.cc
@@ -88,9 +88,6 @@
   double time_to_next_iteration = std::numeric_limits<double>::infinity();
   if (needs_update) {
     const double active_duration = SpecifiedTiming().ActiveDuration();
-    // TODO(yigu): Direction of WorkletAnimation is always forwards based on
-    // the calculation. Need to unify the logic to handle it correctly.
-    // https://crbug.com/896249.
     const AnimationDirection direction =
         (GetAnimation() && GetAnimation()->playbackRate() < 0) ? kBackwards
                                                                : kForwards;
diff --git a/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.cc b/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.cc
index aa9baf7..c0f6f0f 100644
--- a/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.cc
+++ b/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.cc
@@ -18,7 +18,7 @@
 PrepopulatedComputedStylePropertyMap::PrepopulatedComputedStylePropertyMap(
     const Document& document,
     const ComputedStyle& style,
-    Node* styled_node,
+    const Node* styled_node,
     const Vector<CSSPropertyID>& native_properties,
     const Vector<AtomicString>& custom_properties)
     : StylePropertyMapReadOnlyMainThread(), styled_node_(styled_node) {
diff --git a/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h b/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h
index c4419a21..1ff3f4d 100644
--- a/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h
+++ b/third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h
@@ -31,7 +31,7 @@
   PrepopulatedComputedStylePropertyMap(
       const Document&,
       const ComputedStyle&,
-      Node* styled_node,
+      const Node* styled_node,
       const Vector<CSSPropertyID>& native_properties,
       const Vector<AtomicString>& custom_properties);
 
@@ -54,7 +54,7 @@
                             const ComputedStyle&,
                             const AtomicString& property_name);
 
-  Member<Node> styled_node_;
+  Member<const Node> styled_node_;
   HeapHashMap<CSSPropertyID, Member<const CSSValue>> native_values_;
   HeapHashMap<AtomicString, Member<const CSSValue>> custom_values_;
 
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 18f6fd0..90c3634d 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -67,7 +67,7 @@
 
 CSSValue* ComputedStyleUtils::ValueForOffset(const ComputedStyle& style,
                                              const LayoutObject* layout_object,
-                                             Node* styled_node,
+                                             const Node* styled_node,
                                              bool allow_visited_style) {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
@@ -219,7 +219,7 @@
 const CSSValueList* ComputedStyleUtils::ValuesForBackgroundShorthand(
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   CSSValueList* result = CSSValueList::CreateCommaSeparated();
   const FillLayer* curr_layer = &style.BackgroundLayers();
@@ -508,7 +508,7 @@
 }
 
 CSSValue* ComputedStyleUtils::MinWidthOrMinHeightAuto(
-    Node* styled_node,
+    const Node* styled_node,
     const ComputedStyle& style) {
   LayoutObject* layout_object =
       styled_node ? styled_node->GetLayoutObject() : nullptr;
@@ -2204,7 +2204,7 @@
     const StylePropertyShorthand& shorthand,
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   for (unsigned i = 0; i < shorthand.length(); ++i) {
@@ -2221,7 +2221,7 @@
     const StylePropertyShorthand& shorthand,
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   CSSValueList* list = CSSValueList::CreateSlashSeparated();
   for (unsigned i = 0; i < shorthand.length(); ++i) {
@@ -2238,7 +2238,7 @@
     const StylePropertyShorthand& shorthand,
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   // Assume the properties are in the usual order top, right, bottom, left.
@@ -2278,7 +2278,7 @@
     const StylePropertyShorthand& shorthand,
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   const CSSValue* start_value =
       shorthand.properties()[0]->CSSValueFromComputedStyle(
@@ -2299,7 +2299,7 @@
     const StylePropertyShorthand& shorthand,
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   const CSSValue* align_value =
       shorthand.properties()[0]->CSSValueFromComputedStyle(
@@ -2325,7 +2325,7 @@
 CSSValue* ComputedStyleUtils::ValuesForFontVariantProperty(
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) {
   enum VariantShorthandCases {
     kAllNormal,
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.h b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
index 2b3f3d0c..eac5b3cf 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.h
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
@@ -58,7 +58,7 @@
   static const CSSValueList* ValuesForBackgroundShorthand(
       const ComputedStyle&,
       const LayoutObject*,
-      Node*,
+      const Node*,
       bool allow_visited_style);
   static const CSSValue* BackgroundRepeatOrWebkitMaskRepeat(const FillLayer*);
   static const CSSValue* BackgroundPositionOrWebkitMaskPosition(
@@ -85,9 +85,9 @@
 
   static CSSValue* ValueForOffset(const ComputedStyle&,
                                   const LayoutObject*,
-                                  Node*,
+                                  const Node*,
                                   bool allow_visited_style);
-  static CSSValue* MinWidthOrMinHeightAuto(Node*, const ComputedStyle&);
+  static CSSValue* MinWidthOrMinHeightAuto(const Node*, const ComputedStyle&);
   static CSSValue* ValueForPositionOffset(const ComputedStyle&,
                                           const CSSProperty&,
                                           const LayoutObject*);
@@ -179,32 +179,32 @@
   static CSSValueList* ValuesForShorthandProperty(const StylePropertyShorthand&,
                                                   const ComputedStyle&,
                                                   const LayoutObject*,
-                                                  Node*,
+                                                  const Node*,
                                                   bool allow_visited_style);
   static CSSValueList* ValuesForGridShorthand(const StylePropertyShorthand&,
                                               const ComputedStyle&,
                                               const LayoutObject*,
-                                              Node*,
+                                              const Node*,
                                               bool allow_visited_style);
   static CSSValueList* ValuesForSidesShorthand(const StylePropertyShorthand&,
                                                const ComputedStyle&,
                                                const LayoutObject*,
-                                               Node*,
+                                               const Node*,
                                                bool allow_visited_style);
   static CSSValuePair* ValuesForInlineBlockShorthand(
       const StylePropertyShorthand&,
       const ComputedStyle&,
       const LayoutObject*,
-      Node*,
+      const Node*,
       bool allow_visited_style);
   static CSSValuePair* ValuesForPlaceShorthand(const StylePropertyShorthand&,
                                                const ComputedStyle&,
                                                const LayoutObject*,
-                                               Node*,
+                                               const Node*,
                                                bool allow_visited_style);
   static CSSValue* ValuesForFontVariantProperty(const ComputedStyle&,
                                                 const LayoutObject*,
-                                                Node*,
+                                                const Node*,
                                                 bool allow_visited_style);
   static CSSValue* ScrollCustomizationFlagsToCSSValue(
       scroll_customization::ScrollDirection);
diff --git a/third_party/blink/renderer/core/css/properties/css_property.cc b/third_party/blink/renderer/core/css/properties/css_property.cc
index 5726b816..53d8572a 100644
--- a/third_party/blink/renderer/core/css/properties/css_property.cc
+++ b/third_party/blink/renderer/core/css/properties/css_property.cc
@@ -28,10 +28,10 @@
 CSSProperty::CrossThreadStyleValueFromComputedStyle(
     const ComputedStyle& computed_style,
     const LayoutObject* layout_object,
-    Node* node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   const CSSValue* css_value = CSSValueFromComputedStyle(
-      computed_style, layout_object, node, allow_visited_style);
+      computed_style, layout_object, styled_node, allow_visited_style);
   if (!css_value)
     return std::make_unique<CrossThreadUnsupportedValue>("");
   CSSStyleValue* style_value =
@@ -45,7 +45,7 @@
 const CSSValue* CSSProperty::CSSValueFromComputedStyle(
     const ComputedStyle& style,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   const SVGComputedStyle& svg_style = style.SvgStyle();
   const CSSProperty& resolved_property =
diff --git a/third_party/blink/renderer/core/css/properties/css_property.h b/third_party/blink/renderer/core/css/properties/css_property.h
index 6ac5d2f2..acc3dcd 100644
--- a/third_party/blink/renderer/core/css/properties/css_property.h
+++ b/third_party/blink/renderer/core/css/properties/css_property.h
@@ -66,20 +66,20 @@
       const ComputedStyle&,
       const SVGComputedStyle&,
       const LayoutObject*,
-      Node*,
+      const Node*,
       bool allow_visited_style) const {
     return nullptr;
   }
   // TODO: Resolve computed auto alignment in applyProperty/ComputedStyle and
-  // remove this non-const Node parameter.
+  // remove this const Node parameter.
   const CSSValue* CSSValueFromComputedStyle(const ComputedStyle&,
                                             const LayoutObject*,
-                                            Node*,
+                                            const Node*,
                                             bool allow_visited_style) const;
   virtual std::unique_ptr<CrossThreadStyleValue>
   CrossThreadStyleValueFromComputedStyle(const ComputedStyle& computed_style,
                                          const LayoutObject* layout_object,
-                                         Node* node,
+                                         const Node* styled_node,
                                          bool allow_visited_style) const;
   virtual const CSSProperty& ResolveDirectionAwareProperty(TextDirection,
                                                            WritingMode) const {
diff --git a/third_party/blink/renderer/core/css/properties/css_property_methods.json5 b/third_party/blink/renderer/core/css/properties/css_property_methods.json5
index 5fbb9e2..b666b54 100644
--- a/third_party/blink/renderer/core/css/properties/css_property_methods.json5
+++ b/third_party/blink/renderer/core/css/properties/css_property_methods.json5
@@ -32,7 +32,7 @@
     {
       name: "CSSValueFromComputedStyleInternal",
       return_type: "const CSSValue*",
-      parameters: "(const ComputedStyle&, const SVGComputedStyle&, const LayoutObject*, Node*, bool allow_visited_style)",
+      parameters: "(const ComputedStyle&, const SVGComputedStyle&, const LayoutObject*, const Node*, bool allow_visited_style)",
     },
     {
       name: "ColorIncludingFallback",
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc b/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
index fc49720..554dcc5 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
@@ -154,7 +154,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (registration_) {
     const CSSValue* value = style.GetVariableValue(name_, IsInherited());
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property.h b/third_party/blink/renderer/core/css/properties/longhands/custom_property.h
index c7df30f61..a5f80df 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/custom_property.h
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property.h
@@ -45,7 +45,7 @@
       const ComputedStyle&,
       const SVGComputedStyle&,
       const LayoutObject*,
-      Node* styled_node,
+      const Node* styled_node,
       bool allow_visited_style) const override;
 
   bool IsRegistered() const { return registration_; }
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 951e256..a5ae0660 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -72,7 +72,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::
       ValueForContentPositionAndDistributionWithOverflowAlignment(
@@ -95,7 +95,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForItemPositionWithOverflowAlignment(
       style.AlignItems());
@@ -113,7 +113,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForItemPositionWithOverflowAlignment(
       style.AlignSelf());
@@ -122,7 +122,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.AlignmentBaseline());
 }
@@ -139,7 +139,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForAnimationDelay(style.Animations());
 }
@@ -167,7 +167,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const CSSAnimationData* animation_data = style.Animations();
@@ -200,7 +200,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForAnimationDuration(style.Animations());
 }
@@ -228,7 +228,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const CSSAnimationData* animation_data = style.Animations();
@@ -261,7 +261,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const CSSAnimationData* animation_data = style.Animations();
@@ -299,7 +299,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const CSSAnimationData* animation_data = style.Animations();
@@ -334,7 +334,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const CSSAnimationData* animation_data = style.Animations();
@@ -367,7 +367,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForAnimationTimingFunction(
       style.Animations());
@@ -390,7 +390,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFilter(style, style.BackdropFilter());
 }
@@ -398,7 +398,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(
       (style.BackfaceVisibility() == EBackfaceVisibility::kHidden)
@@ -418,7 +418,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   for (const FillLayer* curr_layer = &style.BackgroundLayers(); curr_layer;
@@ -439,7 +439,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   for (const FillLayer* curr_layer = &style.BackgroundLayers(); curr_layer;
@@ -460,7 +460,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const FillLayer* curr_layer = &style.BackgroundLayers();
@@ -490,7 +490,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (allow_visited_style) {
     return cssvalue::CSSColorValue::Create(
@@ -513,7 +513,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer& fill_layer = style.BackgroundLayers();
   return ComputedStyleUtils::BackgroundImageOrWebkitMaskImage(fill_layer);
@@ -531,7 +531,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const FillLayer* curr_layer = &style.BackgroundLayers();
@@ -556,7 +556,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer* curr_layer = &style.BackgroundLayers();
   return ComputedStyleUtils::BackgroundPositionXOrWebkitMaskPositionX(
@@ -577,7 +577,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer* curr_layer = &style.BackgroundLayers();
   return ComputedStyleUtils::BackgroundPositionYOrWebkitMaskPositionY(
@@ -596,7 +596,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer& fill_layer = style.BackgroundLayers();
   return ComputedStyleUtils::BackgroundImageOrWebkitMaskSize(style, fill_layer);
@@ -618,7 +618,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   switch (svg_style.BaselineShift()) {
     case BS_SUPER:
@@ -733,7 +733,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return allow_visited_style ? cssvalue::CSSColorValue::Create(
                                    style.VisitedDependentColor(*this).Rgb())
@@ -752,7 +752,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return &ComputedStyleUtils::ValueForBorderRadiusCorner(
       style.BorderBottomLeftRadius(), style);
@@ -769,7 +769,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return &ComputedStyleUtils::ValueForBorderRadiusCorner(
       style.BorderBottomRightRadius(), style);
@@ -778,7 +778,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BorderBottomStyle());
 }
@@ -794,7 +794,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.BorderBottomWidth(), style);
 }
@@ -803,7 +803,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.BorderCollapse() == EBorderCollapse::kCollapse)
     return CSSIdentifierValue::Create(CSSValueID::kCollapse);
@@ -821,7 +821,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageQuad(
       style.BorderImage().Outset(), style);
@@ -847,7 +847,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageRepeat(style.BorderImage());
 }
@@ -870,7 +870,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageSlice(style.BorderImage());
 }
@@ -898,7 +898,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (style.BorderImageSource())
     return style.BorderImageSource()->ComputedCSSValue();
@@ -928,7 +928,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageQuad(
       style.BorderImage().BorderSlices(), style);
@@ -995,7 +995,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return allow_visited_style ? cssvalue::CSSColorValue::Create(
                                    style.VisitedDependentColor(*this).Rgb())
@@ -1007,7 +1007,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BorderLeftStyle());
 }
@@ -1023,7 +1023,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.BorderLeftWidth(), style);
 }
@@ -1048,7 +1048,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return allow_visited_style ? cssvalue::CSSColorValue::Create(
                                    style.VisitedDependentColor(*this).Rgb())
@@ -1060,7 +1060,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BorderRightStyle());
 }
@@ -1076,7 +1076,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.BorderRightWidth(), style);
 }
@@ -1101,7 +1101,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return allow_visited_style
              ? cssvalue::CSSColorValue::Create(
@@ -1121,7 +1121,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return &ComputedStyleUtils::ValueForBorderRadiusCorner(
       style.BorderTopLeftRadius(), style);
@@ -1138,7 +1138,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return &ComputedStyleUtils::ValueForBorderRadiusCorner(
       style.BorderTopRightRadius(), style);
@@ -1148,7 +1148,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BorderTopStyle());
 }
@@ -1164,7 +1164,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.BorderTopWidth(), style);
 }
@@ -1187,7 +1187,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPositionOffset(style, *this,
                                                     layout_object);
@@ -1205,7 +1205,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForShadowList(style.BoxShadow(), style, true);
 }
@@ -1214,7 +1214,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.BoxSizing() == EBoxSizing::kContentBox)
     return CSSIdentifierValue::Create(CSSValueID::kContentBox);
@@ -1225,7 +1225,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BreakAfter());
 }
@@ -1234,7 +1234,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BreakBefore());
 }
@@ -1243,7 +1243,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BreakInside());
 }
@@ -1252,7 +1252,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.BufferedRendering());
 }
@@ -1261,7 +1261,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.CaptionSide());
 }
@@ -1291,7 +1291,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   blink::Color color;
   if (allow_visited_style)
@@ -1321,7 +1321,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Clear());
 }
@@ -1377,7 +1377,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HasAutoClip())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -1408,7 +1408,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (ClipPathOperation* operation = style.ClipPath()) {
     if (operation->GetType() == ClipPathOperation::SHAPE) {
@@ -1427,7 +1427,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.ClipRule());
 }
@@ -1450,7 +1450,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return cssvalue::CSSColorValue::Create(
       allow_visited_style ? style.VisitedDependentColor(*this).Rgb()
@@ -1491,7 +1491,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.ColorInterpolation());
 }
@@ -1500,7 +1500,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.ColorInterpolationFilters());
 }
@@ -1509,7 +1509,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.ColorRendering());
 }
@@ -1566,7 +1566,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (style.ColorScheme().IsEmpty())
     return CSSIdentifierValue::Create(CSSValueID::kNormal);
@@ -1632,7 +1632,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HasAutoColumnCount())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -1644,7 +1644,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetColumnFill());
 }
@@ -1660,7 +1660,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool) const {
   return ComputedStyleUtils::ValueForGapLength(style.ColumnGap(), style);
 }
@@ -1683,7 +1683,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return allow_visited_style ? cssvalue::CSSColorValue::Create(
                                    style.VisitedDependentColor(*this).Rgb())
@@ -1695,7 +1695,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.ColumnRuleStyle());
 }
@@ -1713,7 +1713,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.ColumnRuleWidth(), style);
 }
@@ -1730,7 +1730,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(static_cast<unsigned>(style.GetColumnSpan())
                                         ? CSSValueID::kAll
@@ -1748,7 +1748,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HasAutoColumnWidth())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -1805,7 +1805,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.Contain())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -1946,7 +1946,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForContentData(style);
 }
@@ -2063,7 +2063,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForCounterDirectives(style, true);
 }
@@ -2082,7 +2082,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForCounterDirectives(style, false);
 }
@@ -2150,7 +2150,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = nullptr;
   CursorList* cursors = style.Cursors();
@@ -2216,7 +2216,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.Cx(),
                                                              style);
@@ -2233,7 +2233,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.Cy(),
                                                              style);
@@ -2249,7 +2249,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (const StylePath* style_path = svg_style.D())
     return style_path->ComputedCSSValue();
@@ -2260,7 +2260,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Direction());
 }
@@ -2306,7 +2306,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (style.IsDisplayLayoutCustomBox()) {
     return MakeGarbageCollected<cssvalue::CSSLayoutFunctionValue>(
@@ -2353,7 +2353,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.DominantBaseline());
 }
@@ -2362,7 +2362,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.EmptyCells());
 }
@@ -2377,7 +2377,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::AdjustSVGPaintForCurrentColor(
       svg_style.FillPaint(), style.GetColor());
@@ -2394,7 +2394,7 @@
     const ComputedStyle&,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(svg_style.FillOpacity(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -2404,7 +2404,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.FillRule());
 }
@@ -2419,7 +2419,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFilter(style, style.Filter());
 }
@@ -2439,7 +2439,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(style.FlexBasis(),
                                                              style);
@@ -2449,7 +2449,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.FlexDirection());
 }
@@ -2465,7 +2465,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.FlexGrow(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -2483,7 +2483,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.FlexShrink(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -2493,7 +2493,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.FlexWrap());
 }
@@ -2502,7 +2502,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HasOutOfFlowPosition())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -2529,7 +2529,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(style,
                                                       style.FloodColor());
@@ -2546,7 +2546,7 @@
     const ComputedStyle&,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(svg_style.FloodOpacity(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -2563,7 +2563,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontFamily(style);
 }
@@ -2579,7 +2579,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const blink::FontFeatureSettings* feature_settings =
       style.GetFontDescription().FeatureSettings();
@@ -2599,7 +2599,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetFontDescription().GetKerning());
 }
@@ -2619,7 +2619,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HasFontSizeAdjust()) {
     return CSSNumericLiteralValue::Create(style.FontSizeAdjust(),
@@ -2639,7 +2639,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontSize(style);
 }
@@ -2655,7 +2655,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontStretch(style);
 }
@@ -2671,7 +2671,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontStyle(style);
 }
@@ -2690,7 +2690,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontVariantCaps(style);
 }
@@ -2716,7 +2716,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontVariantEastAsian(style);
 }
@@ -2743,7 +2743,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontVariantLigatures(style);
 }
@@ -2769,7 +2769,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontVariantNumeric(style);
 }
@@ -2826,7 +2826,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const blink::FontVariationSettings* variation_settings =
       style.GetFontDescription().VariationSettings();
@@ -2854,7 +2854,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFontWeight(style);
 }
@@ -2863,7 +2863,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.ForcedColorAdjust());
 }
@@ -2937,7 +2937,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridTrackSizeList(kForColumns, style);
 }
@@ -2970,7 +2970,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   switch (style.GetGridAutoFlow()) {
@@ -3012,7 +3012,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridTrackSizeList(kForRows, style);
 }
@@ -3028,7 +3028,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridPosition(style.GridColumnEnd());
 }
@@ -3044,7 +3044,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridPosition(style.GridColumnStart());
 }
@@ -3060,7 +3060,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridPosition(style.GridRowEnd());
 }
@@ -3076,7 +3076,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridPosition(style.GridRowStart());
 }
@@ -3111,7 +3111,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.NamedGridAreaRowCount()) {
     DCHECK(!style.NamedGridAreaColumnCount());
@@ -3189,7 +3189,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridTrackList(kForColumns, layout_object,
                                                    style);
@@ -3212,7 +3212,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForGridTrackList(kForRows, layout_object,
                                                    style);
@@ -3234,7 +3234,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (ComputedStyleUtils::WidthOrHeightShouldReturnUsedValue(layout_object)) {
     return ZoomAdjustedPixelValue(
@@ -3248,7 +3248,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetHyphens());
 }
@@ -3273,7 +3273,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.RespectImageOrientation() == kRespectImageOrientation)
     return CSSIdentifierValue::Create(CSSValueID::kFromImage);
@@ -3285,7 +3285,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.ImageRendering());
 }
@@ -3590,7 +3590,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Isolation());
 }
@@ -3612,7 +3612,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::
       ValueForContentPositionAndDistributionWithOverflowAlignment(
@@ -3657,7 +3657,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForItemPositionWithOverflowAlignment(
       style.JustifyItems().GetPosition() == ItemPosition::kAuto
@@ -3677,7 +3677,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForItemPositionWithOverflowAlignment(
       style.JustifySelf());
@@ -3701,7 +3701,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPositionOffset(style, *this,
                                                     layout_object);
@@ -3718,7 +3718,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.LetterSpacing())
     return CSSIdentifierValue::Create(CSSValueID::kNormal);
@@ -3745,7 +3745,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(style,
                                                       style.LightingColor());
@@ -3755,7 +3755,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetLineBreak());
 }
@@ -3771,7 +3771,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForLineHeight(style);
 }
@@ -3788,7 +3788,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.LineHeightStep(), style);
 }
@@ -3804,7 +3804,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.ListStyleImage())
     return style.ListStyleImage()->ComputedCSSValue();
@@ -3821,7 +3821,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.ListStylePosition());
 }
@@ -3830,7 +3830,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.ListStyleType());
 }
@@ -3872,7 +3872,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& margin_bottom = style.MarginBottom();
   if (margin_bottom.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -3920,7 +3920,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& margin_left = style.MarginLeft();
   if (margin_left.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -3950,7 +3950,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& margin_right = style.MarginRight();
   if (margin_right.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -3994,7 +3994,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& margin_top = style.MarginTop();
   if (margin_top.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -4017,7 +4017,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForSVGResource(svg_style.MarkerEndResource());
 }
@@ -4035,7 +4035,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForSVGResource(svg_style.MarkerMidResource());
 }
@@ -4053,7 +4053,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForSVGResource(
       svg_style.MarkerStartResource());
@@ -4071,7 +4071,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForSVGResource(svg_style.MaskerResource());
 }
@@ -4099,7 +4099,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   for (const FillLayer* curr_layer = &style.MaskLayers(); curr_layer;
@@ -4112,7 +4112,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.MaskType());
 }
@@ -4136,7 +4136,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& max_height = style.MaxHeight();
   if (max_height.IsMaxSizeNone())
@@ -4162,7 +4162,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& max_width = style.MaxWidth();
   if (max_width.IsMaxSizeNone())
@@ -4189,7 +4189,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   if (style.MinHeight().IsAuto())
     return ComputedStyleUtils::MinWidthOrMinHeightAuto(styled_node, style);
@@ -4215,7 +4215,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   if (style.MinWidth().IsAuto())
     return ComputedStyleUtils::MinWidthOrMinHeightAuto(styled_node, style);
@@ -4227,7 +4227,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetBlendMode());
 }
@@ -4236,7 +4236,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetObjectFit());
 }
@@ -4254,7 +4254,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return MakeGarbageCollected<CSSValuePair>(
       ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
@@ -4280,7 +4280,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPosition(style.OffsetAnchor(), style);
 }
@@ -4297,7 +4297,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
       style.OffsetDistance(), style);
@@ -4314,7 +4314,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (const BasicShape* style_motion_path = style.OffsetPath())
     return ValueForBasicShape(style, style_motion_path);
@@ -4342,7 +4342,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPosition(style.OffsetPosition(), style);
 }
@@ -4357,7 +4357,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (style.OffsetRotate().type == OffsetRotationType::kAuto)
@@ -4377,7 +4377,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.Opacity(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -4393,7 +4393,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.Order(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -4409,7 +4409,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.Orphans(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -4436,7 +4436,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return allow_visited_style ? cssvalue::CSSColorValue::Create(
                                    style.VisitedDependentColor(*this).Rgb())
@@ -4456,7 +4456,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.OutlineOffset(), style);
 }
@@ -4465,7 +4465,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.OutlineStyleIsAuto())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -4505,7 +4505,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.OutlineWidth(), style);
 }
@@ -4514,7 +4514,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.OverflowAnchor());
 }
@@ -4523,7 +4523,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.OverflowWrap());
 }
@@ -4532,7 +4532,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.OverflowX());
 }
@@ -4541,7 +4541,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.OverflowY());
 }
@@ -4550,7 +4550,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.OverscrollBehaviorX());
 }
@@ -4559,7 +4559,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.OverscrollBehaviorY());
 }
@@ -4601,7 +4601,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& padding_bottom = style.PaddingBottom();
   if (padding_bottom.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -4649,7 +4649,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& padding_left = style.PaddingLeft();
   if (padding_left.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -4679,7 +4679,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& padding_right = style.PaddingRight();
   if (padding_right.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -4709,7 +4709,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& padding_top = style.PaddingTop();
   if (padding_top.IsFixed() || !layout_object || !layout_object->IsBox()) {
@@ -4785,7 +4785,7 @@
     const ComputedStyle&,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const EPaintOrder paint_order = svg_style.PaintOrder();
   if (paint_order == kPaintOrderNormal)
@@ -4846,7 +4846,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.HasPerspective())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -4871,7 +4871,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (layout_object) {
     LayoutRect box;
@@ -4900,7 +4900,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.PointerEvents());
 }
@@ -4909,7 +4909,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetPosition());
 }
@@ -4941,7 +4941,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.Quotes()) {
     // TODO(ramya.v): We should return the quote values that we're actually
@@ -4972,7 +4972,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.R(),
                                                              style);
@@ -4982,7 +4982,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Resize());
 }
@@ -5022,7 +5022,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPositionOffset(style, *this,
                                                     layout_object);
@@ -5063,7 +5063,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.Rotate())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -5093,7 +5093,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool) const {
   return ComputedStyleUtils::ValueForGapLength(style.RowGap(), style);
 }
@@ -5111,7 +5111,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.Rx(),
                                                              style);
@@ -5130,7 +5130,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.Ry(),
                                                              style);
@@ -5174,7 +5174,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   ScaleTransformOperation* scale = style.Scale();
   if (!scale)
@@ -5202,7 +5202,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetScrollBehavior());
 }
@@ -5258,7 +5258,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ScrollCustomizationFlagsToCSSValue(
       style.ScrollCustomization());
@@ -5292,7 +5292,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.ScrollMarginBottom(), style);
 }
@@ -5325,7 +5325,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.ScrollMarginLeft(), style);
 }
@@ -5342,7 +5342,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.ScrollMarginRight(), style);
 }
@@ -5359,7 +5359,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.ScrollMarginTop(), style);
 }
@@ -5395,7 +5395,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
       style.ScrollPaddingBottom(), style);
@@ -5432,7 +5432,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
       style.ScrollPaddingLeft(), style);
@@ -5451,7 +5451,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
       style.ScrollPaddingRight(), style);
@@ -5470,7 +5470,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
       style.ScrollPaddingTop(), style);
@@ -5502,7 +5502,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForScrollSnapAlign(style.GetScrollSnapAlign(),
                                                      style);
@@ -5512,7 +5512,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.ScrollSnapStop());
 }
@@ -5546,7 +5546,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForScrollSnapType(style.GetScrollSnapType(),
                                                     style);
@@ -5563,7 +5563,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.ShapeImageThreshold(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -5581,7 +5581,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return CSSValue::Create(style.ShapeMargin(), style.EffectiveZoom());
 }
@@ -5613,7 +5613,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForShape(style, style.ShapeOutside());
 }
@@ -5622,7 +5622,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.ShapeRendering());
 }
@@ -5771,7 +5771,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Speak());
 }
@@ -5796,7 +5796,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(style, style.StopColor());
 }
@@ -5812,7 +5812,7 @@
     const ComputedStyle&,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(svg_style.StopOpacity(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -5828,7 +5828,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::AdjustSVGPaintForCurrentColor(
       svg_style.StrokePaint(), style.GetColor());
@@ -5860,7 +5860,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::StrokeDashArrayToCSSValueList(
       *svg_style.StrokeDashArray(), style);
@@ -5879,7 +5879,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
       style.StrokeDashOffset(), style);
@@ -5889,7 +5889,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.CapStyle());
 }
@@ -5898,7 +5898,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.JoinStyle());
 }
@@ -5915,7 +5915,7 @@
     const ComputedStyle&,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(svg_style.StrokeMiterLimit(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -5932,7 +5932,7 @@
     const ComputedStyle&,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(svg_style.StrokeOpacity(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -5951,7 +5951,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const Length& length = svg_style.StrokeWidth().length();
   if (length.IsFixed()) {
@@ -5976,7 +5976,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(
       style.GetTabSize().GetPixelSize(1.0),
@@ -5988,7 +5988,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.TableLayout());
 }
@@ -5997,7 +5997,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetTextAlign());
 }
@@ -6033,7 +6033,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.TextAlignLast());
 }
@@ -6042,7 +6042,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.TextAnchor());
 }
@@ -6051,7 +6051,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.TextCombine());
 }
@@ -6075,7 +6075,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(
       style, style.TextDecorationColor());
@@ -6092,7 +6092,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::RenderTextDecorationFlagsToCSSValue(
       style.GetTextDecoration());
@@ -6102,7 +6102,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForTextDecorationSkipInk(
       style.TextDecorationSkipInk());
@@ -6112,7 +6112,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForTextDecorationStyle(
       style.TextDecorationStyle());
@@ -6169,7 +6169,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   list->Append(*ComputedStyleUtils::ZoomAdjustedPixelValueForLength(
@@ -6230,7 +6230,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetTextJustify());
 }
@@ -6239,7 +6239,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetTextOrientation());
 }
@@ -6254,7 +6254,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.TextOverflow() != ETextOverflow::kClip)
     return CSSIdentifierValue::Create(CSSValueID::kEllipsis);
@@ -6265,7 +6265,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetFontDescription().TextRendering());
 }
@@ -6282,7 +6282,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForShadowList(style.TextShadow(), style,
                                                 false);
@@ -6304,7 +6304,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.GetTextSizeAdjust().IsAuto())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -6317,7 +6317,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.TextTransform());
 }
@@ -6354,7 +6354,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   auto text_underline_position = style.TextUnderlinePosition();
   if (text_underline_position == kTextUnderlinePositionAuto)
@@ -6395,7 +6395,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPositionOffset(style, *this,
                                                     layout_object);
@@ -6461,7 +6461,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::TouchActionFlagsToCSSValue(style.GetTouchAction());
 }
@@ -6470,7 +6470,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.TransformBox());
 }
@@ -6492,7 +6492,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ComputedTransform(layout_object, style);
 }
@@ -6529,7 +6529,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   if (layout_object) {
@@ -6555,7 +6555,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(
       (style.TransformStyle3D() == ETransformStyle3D::kPreserve3d)
@@ -6575,7 +6575,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForAnimationDelay(style.Transitions());
 }
@@ -6600,7 +6600,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForAnimationDuration(style.Transitions());
 }
@@ -6628,7 +6628,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForTransitionProperty(style.Transitions());
 }
@@ -6651,7 +6651,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForAnimationTimingFunction(
       style.Transitions());
@@ -6705,7 +6705,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.Translate())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -6729,7 +6729,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetUnicodeBidi());
 }
@@ -6738,7 +6738,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.UserSelect());
 }
@@ -6747,7 +6747,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(svg_style.VectorEffect());
 }
@@ -6770,7 +6770,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   switch (style.VerticalAlign()) {
     case EVerticalAlign::kBaseline:
@@ -6824,7 +6824,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Visibility());
 }
@@ -6833,7 +6833,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (style.DraggableRegionMode() == EDraggableRegionMode::kNone)
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -6861,7 +6861,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.Appearance());
 }
@@ -6879,7 +6879,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.HorizontalBorderSpacing(), style);
 }
@@ -6895,7 +6895,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImage(style.BorderImage(), style);
 }
@@ -6920,7 +6920,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.VerticalBorderSpacing(), style);
 }
@@ -6929,7 +6929,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BoxAlign());
 }
@@ -6938,7 +6938,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.BoxDecorationBreak() == EBoxDecorationBreak::kSlice)
     return CSSIdentifierValue::Create(CSSValueID::kSlice);
@@ -6949,7 +6949,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BoxDirection());
 }
@@ -6965,7 +6965,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.BoxFlex(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -6982,7 +6982,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.BoxOrdinalGroup(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -6992,7 +6992,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BoxOrient());
 }
@@ -7001,7 +7001,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.BoxPack());
 }
@@ -7051,7 +7051,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForReflection(style.BoxReflect(), style);
 }
@@ -7069,7 +7069,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetFontDescription().FontSmoothing());
 }
@@ -7087,7 +7087,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.Highlight() == g_null_atom)
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -7107,7 +7107,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HyphenationString().IsNull())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -7118,7 +7118,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetLineBreak());
 }
@@ -7135,7 +7135,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (!style.HasLineClamp())
     return CSSIdentifierValue::Create(CSSValueID::kNone);
@@ -7156,7 +7156,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.Locale().IsNull())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -7178,7 +7178,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.MarginAfterCollapse());
 }
@@ -7187,7 +7187,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.MarginBeforeCollapse());
 }
@@ -7196,7 +7196,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.MarginAfterCollapse());
 }
@@ -7205,7 +7205,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.MarginBeforeCollapse());
 }
@@ -7221,7 +7221,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageQuad(
       style.MaskBoxImage().Outset(), style);
@@ -7238,7 +7238,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageRepeat(style.MaskBoxImage());
 }
@@ -7255,7 +7255,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageSlice(style.MaskBoxImage());
 }
@@ -7271,7 +7271,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   if (style.MaskBoxImageSource())
     return style.MaskBoxImageSource()->ComputedCSSValue();
@@ -7295,7 +7295,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImageQuad(
       style.MaskBoxImage().BorderSlices(), style);
@@ -7314,7 +7314,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const FillLayer* curr_layer = &style.MaskLayers();
@@ -7337,7 +7337,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const FillLayer* curr_layer = &style.MaskLayers();
@@ -7358,7 +7358,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer& fill_layer = style.MaskLayers();
   return ComputedStyleUtils::BackgroundImageOrWebkitMaskImage(fill_layer);
@@ -7377,7 +7377,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateCommaSeparated();
   const FillLayer* curr_layer = &style.MaskLayers();
@@ -7402,7 +7402,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer* curr_layer = &style.MaskLayers();
   return ComputedStyleUtils::BackgroundPositionXOrWebkitMaskPositionX(
@@ -7423,7 +7423,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer* curr_layer = &style.MaskLayers();
   return ComputedStyleUtils::BackgroundPositionYOrWebkitMaskPositionY(
@@ -7442,7 +7442,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   const FillLayer& fill_layer = style.MaskLayers();
   return ComputedStyleUtils::BackgroundImageOrWebkitMaskSize(style, fill_layer);
@@ -7470,7 +7470,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.PrintColorAdjust());
 }
@@ -7479,7 +7479,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.RtlOrdering() == EOrder::kVisual
                                         ? CSSValueID::kVisual
@@ -7490,7 +7490,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetRubyPosition());
 }
@@ -7515,7 +7515,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(
       style, style.TapHighlightColor());
@@ -7525,7 +7525,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.TextCombine() == ETextCombine::kAll)
     return CSSIdentifierValue::Create(CSSValueID::kHorizontal);
@@ -7544,7 +7544,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::RenderTextDecorationFlagsToCSSValue(
       style.TextDecorationsInEffect());
@@ -7568,7 +7568,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(
       style, style.TextEmphasisColor());
@@ -7628,7 +7628,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   switch (style.GetTextEmphasisPosition()) {
@@ -7691,7 +7691,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   switch (style.GetTextEmphasisMark()) {
     case TextEmphasisMark::kNone:
@@ -7796,7 +7796,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(style,
                                                       style.TextFillColor());
@@ -7806,7 +7806,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.GetTextOrientation() == ETextOrientation::kMixed)
     return CSSIdentifierValue::Create(CSSValueID::kVerticalRight);
@@ -7823,7 +7823,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.TextSecurity());
 }
@@ -7846,7 +7846,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::CurrentColorOrValidColor(style,
                                                       style.TextStrokeColor());
@@ -7865,7 +7865,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.TextStrokeWidth(), style);
 }
@@ -7900,7 +7900,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.UserDrag());
 }
@@ -7909,7 +7909,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.UserModify());
 }
@@ -7918,7 +7918,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetWritingMode());
 }
@@ -7933,7 +7933,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.WhiteSpace());
 }
@@ -7948,7 +7948,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.Widows(),
                                         CSSPrimitiveValue::UnitType::kNumber);
@@ -7970,7 +7970,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (ComputedStyleUtils::WidthOrHeightShouldReturnUsedValue(layout_object)) {
     return ZoomAdjustedPixelValue(
@@ -8041,7 +8041,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForWillChange(
       style.WillChangeProperties(), style.WillChangeContents(),
@@ -8102,7 +8102,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.WordBreak());
 }
@@ -8118,7 +8118,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ZoomAdjustedPixelValue(style.WordSpacing(), style);
 }
@@ -8127,7 +8127,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSIdentifierValue::Create(style.GetWritingMode());
 }
@@ -8149,7 +8149,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.X(),
                                                              style);
@@ -8166,7 +8166,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle& svg_style,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ZoomAdjustedPixelValueForLength(svg_style.Y(),
                                                              style);
@@ -8184,7 +8184,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   if (style.HasAutoZIndex() || !style.IsStackingContext())
     return CSSIdentifierValue::Create(CSSValueID::kAuto);
@@ -8223,7 +8223,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return CSSNumericLiteralValue::Create(style.Zoom(),
                                         CSSPrimitiveValue::UnitType::kNumber);
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
index 4679ae1..e60f650 100644
--- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -105,7 +105,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const CSSAnimationData* animation_data = style.Animations();
   if (animation_data) {
@@ -172,7 +172,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForBackgroundShorthand(
       style, layout_object, styled_node, allow_visited_style);
@@ -211,7 +211,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::BackgroundPositionOrWebkitMaskPosition(
       *this, style, &style.BackgroundLayers());
@@ -251,7 +251,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::BackgroundRepeatOrWebkitMaskRepeat(
       &style.BackgroundLayers());
@@ -271,7 +271,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       borderBlockColorShorthand(), style, layout_object, styled_node,
@@ -307,7 +307,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   const CSSValue* value_start =
       GetCSSPropertyBorderBlockStart().CSSValueFromComputedStyle(
@@ -355,7 +355,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       borderBlockStyleShorthand(), style, layout_object, styled_node,
@@ -376,7 +376,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       borderBlockWidthShorthand(), style, layout_object, styled_node,
@@ -397,7 +397,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       borderBottomShorthand(), style, layout_object, styled_node,
@@ -418,7 +418,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(
       borderColorShorthand(), style, layout_object, styled_node,
@@ -457,7 +457,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   const CSSValue* value = GetCSSPropertyBorderTop().CSSValueFromComputedStyle(
       style, layout_object, styled_node, allow_visited_style);
@@ -533,7 +533,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImage(style.BorderImage(), style);
 }
@@ -552,7 +552,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       borderInlineColorShorthand(), style, layout_object, styled_node,
@@ -588,7 +588,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   const CSSValue* value_start =
       GetCSSPropertyBorderInlineStart().CSSValueFromComputedStyle(
@@ -636,7 +636,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       borderInlineStyleShorthand(), style, layout_object, styled_node,
@@ -657,7 +657,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       borderInlineWidthShorthand(), style, layout_object, styled_node,
@@ -678,7 +678,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       borderLeftShorthand(), style, layout_object, styled_node,
@@ -734,7 +734,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForBorderRadiusShorthand(style);
 }
@@ -753,7 +753,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       borderRightShorthand(), style, layout_object, styled_node,
@@ -796,7 +796,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   list->Append(*ZoomAdjustedPixelValue(style.HorizontalBorderSpacing(), style));
@@ -818,7 +818,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(
       borderStyleShorthand(), style, layout_object, styled_node,
@@ -839,7 +839,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       borderTopShorthand(), style, layout_object, styled_node,
@@ -860,7 +860,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(
       borderWidthShorthand(), style, layout_object, styled_node,
@@ -881,7 +881,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       columnRuleShorthand(), style, layout_object, styled_node,
@@ -922,7 +922,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       columnsShorthand(), style, layout_object, styled_node,
@@ -1014,7 +1014,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       flexShorthand(), style, layout_object, styled_node, allow_visited_style);
@@ -1034,7 +1034,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       flexFlowShorthand(), style, layout_object, styled_node,
@@ -1275,7 +1275,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForFont(style);
 }
@@ -1385,7 +1385,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForFontVariantProperty(
       style, layout_object, styled_node, allow_visited_style);
@@ -1418,7 +1418,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       gapShorthand(), style, layout_object, styled_node, allow_visited_style);
@@ -1497,7 +1497,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForGridShorthand(gridAreaShorthand(), style,
                                                     layout_object, styled_node,
@@ -1539,7 +1539,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForGridShorthand(
       gridColumnShorthand(), style, layout_object, styled_node,
@@ -1567,7 +1567,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       gridColumnGapShorthand(), style, layout_object, styled_node,
@@ -1762,7 +1762,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForGridShorthand(
       gridShorthand(), style, layout_object, styled_node, allow_visited_style);
@@ -1796,7 +1796,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       gridGapShorthand(), style, layout_object, styled_node,
@@ -1838,7 +1838,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForGridShorthand(gridRowShorthand(), style,
                                                     layout_object, styled_node,
@@ -1866,7 +1866,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       gridRowGapShorthand(), style, layout_object, styled_node,
@@ -1919,7 +1919,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForGridShorthand(
       gridTemplateShorthand(), style, layout_object, styled_node,
@@ -1940,7 +1940,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       insetBlockShorthand(), style, layout_object, styled_node,
@@ -1961,7 +1961,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(
       insetShorthand(), style, layout_object, styled_node, allow_visited_style);
@@ -1981,7 +1981,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       insetInlineShorthand(), style, layout_object, styled_node,
@@ -2080,7 +2080,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       listStyleShorthand(), style, layout_object, styled_node,
@@ -2101,7 +2101,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       marginBlockShorthand(), style, layout_object, styled_node,
@@ -2130,7 +2130,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(marginShorthand(), style,
                                                      layout_object, styled_node,
@@ -2151,7 +2151,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       marginInlineShorthand(), style, layout_object, styled_node,
@@ -2302,7 +2302,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForOffset(style, layout_object, styled_node,
                                             allow_visited_style);
@@ -2322,7 +2322,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       outlineShorthand(), style, layout_object, styled_node,
@@ -2343,7 +2343,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   list->Append(*CSSIdentifierValue::Create(style.OverflowX()));
@@ -2367,7 +2367,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   CSSValueList* list = CSSValueList::CreateSpaceSeparated();
   list->Append(*CSSIdentifierValue::Create(style.OverscrollBehaviorX()));
@@ -2391,7 +2391,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       paddingBlockShorthand(), style, layout_object, styled_node,
@@ -2420,7 +2420,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(paddingShorthand(), style,
                                                      layout_object, styled_node,
@@ -2441,7 +2441,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       paddingInlineShorthand(), style, layout_object, styled_node,
@@ -2472,7 +2472,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPageBreakBetween(style.BreakAfter());
 }
@@ -2501,7 +2501,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPageBreakBetween(style.BreakBefore());
 }
@@ -2529,7 +2529,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForPageBreakInside(style.BreakInside());
 }
@@ -2589,7 +2589,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForPlaceShorthand(
       placeContentShorthand(), style, layout_object, styled_node,
@@ -2641,7 +2641,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForPlaceShorthand(
       placeItemsShorthand(), style, layout_object, styled_node,
@@ -2692,7 +2692,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForPlaceShorthand(
       placeSelfShorthand(), style, layout_object, styled_node,
@@ -2713,7 +2713,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       scrollMarginBlockShorthand(), style, layout_object, styled_node,
@@ -2734,7 +2734,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(
       scrollMarginShorthand(), style, layout_object, styled_node,
@@ -2755,7 +2755,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       scrollMarginInlineShorthand(), style, layout_object, styled_node,
@@ -2776,7 +2776,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       scrollPaddingBlockShorthand(), style, layout_object, styled_node,
@@ -2797,7 +2797,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForSidesShorthand(
       scrollPaddingShorthand(), style, layout_object, styled_node,
@@ -2818,7 +2818,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForInlineBlockShorthand(
       scrollPaddingInlineShorthand(), style, layout_object, styled_node,
@@ -2839,7 +2839,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject* layout_object,
-    Node* styled_node,
+    const Node* styled_node,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValuesForShorthandProperty(
       textDecorationShorthand(), style, layout_object, styled_node,
@@ -2909,7 +2909,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   const CSSTransitionData* transition_data = style.Transitions();
   if (transition_data) {
@@ -2969,7 +2969,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForWebkitColumnBreakBetween(
       style.BreakAfter());
@@ -2998,7 +2998,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForWebkitColumnBreakBetween(
       style.BreakBefore());
@@ -3027,7 +3027,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node* styled_node,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForWebkitColumnBreakInside(
       style.BreakInside());
@@ -3128,7 +3128,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::ValueForNinePieceImage(style.MaskBoxImage(),
                                                     style);
@@ -3177,7 +3177,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::BackgroundPositionOrWebkitMaskPosition(
       *this, style, &style.MaskLayers());
@@ -3217,7 +3217,7 @@
     const ComputedStyle& style,
     const SVGComputedStyle&,
     const LayoutObject*,
-    Node*,
+    const Node*,
     bool allow_visited_style) const {
   return ComputedStyleUtils::BackgroundRepeatOrWebkitMaskRepeat(
       &style.MaskLayers());
diff --git a/third_party/blink/renderer/core/html/parser/html_resource_preloader.cc b/third_party/blink/renderer/core/html/parser/html_resource_preloader.cc
index c1af390da..cfd414d 100644
--- a/third_party/blink/renderer/core/html/parser/html_resource_preloader.cc
+++ b/third_party/blink/renderer/core/html/parser/html_resource_preloader.cc
@@ -115,27 +115,25 @@
     case ResourceType::kVideo:
     case ResourceType::kManifest:
     case ResourceType::kMock:
-      if (GetFieldTrialParamByFeatureAsBool(
-              features::kLightweightNoStatePrefetch, "skip_other", false)) {
-        return false;
-      }
-      break;
+      return !GetFieldTrialParamByFeatureAsBool(
+          features::kLightweightNoStatePrefetch, "skip_other", false);
     case ResourceType::kImage:
       return false;
     case ResourceType::kCSSStyleSheet:
-      break;
+      return true;
     case ResourceType::kScript:
+      // We might skip all script.
       if (GetFieldTrialParamByFeatureAsBool(
               features::kLightweightNoStatePrefetch, "skip_script", false)) {
-        // TODO(ryansturm): Add an arm to block async script only.
-        // https://crbug.com/934466
         return false;
       }
-      break;
-  }
 
-  // Skip lazy-loaded resources.
-  return preload->DeferOption() == FetchParameters::DeferOption::kNoDefer;
+      // Otherwise, we might skip async/deferred script.
+      return !GetFieldTrialParamByFeatureAsBool(
+                 features::kLightweightNoStatePrefetch, "skip_async_script",
+                 false) ||
+             preload->DeferOption() == FetchParameters::DeferOption::kNoDefer;
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 265590d..1537721 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -6352,12 +6352,19 @@
       yuv422
       yuv444
 
+  # Image format of a given image.
+  type ImageType extends string
+    enum
+      jpeg
+      webp
+      unknown
+
   # Describes a supported image decoding profile with its associated minimum and
   # maximum resolutions and subsampling.
   type ImageDecodeAcceleratorCapability extends object
     properties
       # Image coded, e.g. Jpeg.
-      string imageType
+      ImageType imageType
       # Maximum supported dimensions of the image in pixels.
       Size maxDimensions
       # Minimum supported dimensions of the image in pixels.
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
index 18b48cc8..72375233 100644
--- a/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
+++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
@@ -211,7 +211,13 @@
 export function showToast(message, options = {}) {
   const toast = new StdToastElement(message);
 
-  const {action, closeButton, ...showOptions} = options;
+  const {
+    action,
+    closeButton,
+    type,
+    ...showOptions
+  } = options;
+
   if (isElement(action)) {
     toast.action = action;
   } else if (action !== undefined) {
@@ -229,6 +235,10 @@
     toast.closeButton = closeButton;
   }
 
+  if (type !== undefined) {
+    toast.type = type;
+  }
+
   document.body.append(toast);
   toast.show(showOptions);
 
diff --git a/third_party/blink/renderer/core/scroll/scroll_animator.cc b/third_party/blink/renderer/core/scroll/scroll_animator.cc
index d7a17d9d..4f3344e 100644
--- a/third_party/blink/renderer/core/scroll/scroll_animator.cc
+++ b/third_party/blink/renderer/core/scroll/scroll_animator.cc
@@ -431,27 +431,6 @@
     std::move(on_finish_).Run();
 }
 
-void ScrollAnimator::NotifyAnimationTakeover(
-    double monotonic_time,
-    double animation_start_time,
-    std::unique_ptr<cc::AnimationCurve> curve) {
-  // If there is already an animation running and the compositor asks to take
-  // over an animation, do nothing to avoid judder.
-  if (HasRunningAnimation())
-    return;
-
-  cc::ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
-      curve->ToScrollOffsetAnimationCurve();
-  ScrollOffset target_value(scroll_offset_animation_curve->target_value().x(),
-                            scroll_offset_animation_curve->target_value().y());
-  if (WillAnimateToOffset(target_value)) {
-    animation_curve_ = std::make_unique<CompositorScrollOffsetAnimationCurve>(
-        scroll_offset_animation_curve);
-    start_time_ =
-        base::TimeTicks() + base::TimeDelta::FromSecondsD(animation_start_time);
-  }
-}
-
 void ScrollAnimator::CancelAnimation() {
   ScrollAnimatorCompositorCoordinator::CancelAnimation();
   if (on_finish_)
diff --git a/third_party/blink/renderer/core/scroll/scroll_animator.h b/third_party/blink/renderer/core/scroll/scroll_animator.h
index 64a772b..110333f 100644
--- a/third_party/blink/renderer/core/scroll/scroll_animator.h
+++ b/third_party/blink/renderer/core/scroll/scroll_animator.h
@@ -137,14 +137,6 @@
   // Returns whether or not the animation was sent to the compositor.
   virtual bool SendAnimationToCompositor();
 
-  void NotifyAnimationTakeover(double monotonic_time,
-                               double animation_start_time,
-                               std::unique_ptr<cc::AnimationCurve>) override;
-
-  std::unique_ptr<CompositorScrollOffsetAnimationCurve> animation_curve_;
-  const base::TickClock* const tick_clock_;
-  base::TimeTicks start_time_;
-
  private:
   // Returns true if the animation was scheduled successfully. If animation
   // could not be scheduled (e.g. because the frame is detached), scrolls
@@ -162,6 +154,10 @@
   // because we are already at targetPos.
   bool WillAnimateToOffset(const ScrollOffset& target_pos);
 
+  std::unique_ptr<CompositorScrollOffsetAnimationCurve> animation_curve_;
+  const base::TickClock* const tick_clock_;
+  base::TimeTicks start_time_;
+
   ScrollOffset target_offset_;
   ScrollGranularity last_granularity_;
 
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index b2388cc..a61f1561 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -62,6 +62,10 @@
     if (RuntimeEnabledFeatures::EventTimingEnabled(execution_context))
       supportedEntryTypes.push_back(performance_entry_names::kEvent);
     supportedEntryTypes.push_back(performance_entry_names::kFirstInput);
+    if (RuntimeEnabledFeatures::LargestContentfulPaintEnabled()) {
+      supportedEntryTypes.push_back(
+          performance_entry_names::kLargestContentfulPaint);
+    }
     if (RuntimeEnabledFeatures::LayoutInstabilityAPIEnabled(execution_context))
       supportedEntryTypes.push_back(performance_entry_names::kLayoutShift);
     supportedEntryTypes.push_back(performance_entry_names::kLongtask);
diff --git a/third_party/blink/renderer/core/timing/profiler_group.cc b/third_party/blink/renderer/core/timing/profiler_group.cc
index 4e89914..698926c 100644
--- a/third_party/blink/renderer/core/timing/profiler_group.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group.cc
@@ -46,6 +46,10 @@
   return profiler_group;
 }
 
+base::TimeDelta ProfilerGroup::GetBaseSampleInterval() {
+  return base::TimeDelta::FromMilliseconds(kBaseSampleIntervalMs);
+}
+
 ProfilerGroup::ProfilerGroup(v8::Isolate* isolate)
     : isolate_(isolate),
       cpu_profiler_(nullptr),
@@ -61,8 +65,13 @@
   DCHECK_EQ(script_state->GetIsolate(), isolate_);
   DCHECK(init_options.hasSampleInterval());
 
-  if (init_options.sampleInterval() < 0) {
-    exception_state.ThrowRangeError("Expected non-negative sample interval");
+  const base::TimeDelta sample_interval =
+      base::TimeDelta::FromMillisecondsD(init_options.sampleInterval());
+  const int64_t sample_interval_us = sample_interval.InMicroseconds();
+
+  if (sample_interval_us < 0 ||
+      sample_interval_us > std::numeric_limits<int>::max()) {
+    exception_state.ThrowRangeError("Invalid sample interval");
     return nullptr;
   }
 
@@ -70,14 +79,12 @@
     InitV8Profiler();
   DCHECK(cpu_profiler_);
 
-  // TODO(acomminos): Requires support in V8 for subsampling intervals
-  int sample_interval_ms = kBaseSampleIntervalMs;
-
   String profiler_id = NextProfilerId();
-  v8::CpuProfilingOptions options(
-      v8::kLeafNodeLineNumbers, init_options.hasMaxBufferSize()
-                                    ? init_options.maxBufferSize()
-                                    : v8::CpuProfilingOptions::kNoSampleLimit);
+  v8::CpuProfilingOptions options(v8::kLeafNodeLineNumbers,
+                                  init_options.hasMaxBufferSize()
+                                      ? init_options.maxBufferSize()
+                                      : v8::CpuProfilingOptions::kNoSampleLimit,
+                                  static_cast<int>(sample_interval_us));
 
   cpu_profiler_->StartProfiling(V8String(isolate_, profiler_id), options);
 
@@ -87,8 +94,21 @@
   scoped_refptr<const SecurityOrigin> source_origin(
       execution_context->GetSecurityOrigin());
 
-  auto* profiler = MakeGarbageCollected<Profiler>(
-      this, profiler_id, sample_interval_ms, source_origin, time_origin);
+  // The V8 CPU profiler ticks in multiples of the base sampling interval. This
+  // effectively means that we gather samples at the multiple of the base
+  // sampling interval that's greater than or equal to the requested interval.
+  int effective_sample_interval_ms =
+      static_cast<int>(sample_interval.InMilliseconds());
+  if (effective_sample_interval_ms % kBaseSampleIntervalMs != 0 ||
+      effective_sample_interval_ms == 0) {
+    effective_sample_interval_ms +=
+        (kBaseSampleIntervalMs -
+         effective_sample_interval_ms % kBaseSampleIntervalMs);
+  }
+
+  auto* profiler = MakeGarbageCollected<Profiler>(this, profiler_id,
+                                                  effective_sample_interval_ms,
+                                                  source_origin, time_origin);
   profilers_.insert(profiler);
 
   num_active_profilers_++;
diff --git a/third_party/blink/renderer/core/timing/profiler_group.h b/third_party/blink/renderer/core/timing/profiler_group.h
index 1ec3e23..8a74fdc 100644
--- a/third_party/blink/renderer/core/timing/profiler_group.h
+++ b/third_party/blink/renderer/core/timing/profiler_group.h
@@ -30,6 +30,8 @@
  public:
   static ProfilerGroup* From(v8::Isolate*);
 
+  static base::TimeDelta GetBaseSampleInterval();
+
   ProfilerGroup(v8::Isolate* isolate);
   ~ProfilerGroup() override;
 
diff --git a/third_party/blink/renderer/core/timing/profiler_group_test.cc b/third_party/blink/renderer/core/timing/profiler_group_test.cc
index 410cc91a..c54cdfe 100644
--- a/third_party/blink/renderer/core/timing/profiler_group_test.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group_test.cc
@@ -62,4 +62,86 @@
   EXPECT_TRUE(profiler->stopped());
 }
 
+TEST(ProfilerGroupTest, CreateProfiler) {
+  V8TestingScope scope;
+
+  ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+  ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+  init_options->setSampleInterval(10);
+  Profiler* profiler = profiler_group->CreateProfiler(
+      scope.GetScriptState(), *init_options, base::TimeTicks(),
+      scope.GetExceptionState());
+
+  EXPECT_FALSE(profiler->stopped());
+  EXPECT_FALSE(scope.GetExceptionState().HadException());
+}
+
+TEST(ProfilerGroupTest, ClampedSamplingIntervalZero) {
+  V8TestingScope scope;
+
+  ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+  ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+  init_options->setSampleInterval(0);
+  Profiler* profiler = profiler_group->CreateProfiler(
+      scope.GetScriptState(), *init_options, base::TimeTicks(),
+      scope.GetExceptionState());
+
+  EXPECT_FALSE(profiler->stopped());
+  EXPECT_FALSE(scope.GetExceptionState().HadException());
+  // Verify that the sample interval clamped to the next non-zero supported
+  // interval.
+  EXPECT_EQ(profiler->sampleInterval(),
+            ProfilerGroup::GetBaseSampleInterval().InMilliseconds());
+}
+
+TEST(ProfilerGroupTest, ClampedSamplingIntervalNext) {
+  V8TestingScope scope;
+
+  ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+  ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+  init_options->setSampleInterval((ProfilerGroup::GetBaseSampleInterval() +
+                                   base::TimeDelta::FromMilliseconds(1))
+                                      .InMilliseconds());
+  Profiler* profiler = profiler_group->CreateProfiler(
+      scope.GetScriptState(), *init_options, base::TimeTicks(),
+      scope.GetExceptionState());
+
+  EXPECT_FALSE(profiler->stopped());
+  EXPECT_FALSE(scope.GetExceptionState().HadException());
+  // Verify that the sample interval clamped to the next highest supported
+  // interval.
+  EXPECT_EQ(profiler->sampleInterval(),
+            (ProfilerGroup::GetBaseSampleInterval() * 2).InMilliseconds());
+}
+
+TEST(ProfilerGroupTest, NegativeSamplingInterval) {
+  V8TestingScope scope;
+
+  ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+  ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+  init_options->setSampleInterval(-10);
+  profiler_group->CreateProfiler(scope.GetScriptState(), *init_options,
+                                 base::TimeTicks(), scope.GetExceptionState());
+
+  EXPECT_TRUE(scope.GetExceptionState().HadException());
+}
+
+TEST(ProfilerGroupTest, OverflowSamplingInterval) {
+  V8TestingScope scope;
+
+  ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+  ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+  init_options->setSampleInterval((double)std::numeric_limits<int>::max() +
+                                  1.f);
+  profiler_group->CreateProfiler(scope.GetScriptState(), *init_options,
+                                 base::TimeTicks(), scope.GetExceptionState());
+
+  EXPECT_TRUE(scope.GetExceptionState().HadException());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index 90f6062a..cde029d 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -692,6 +692,10 @@
 //
 // Changing scroll-linked animation start_time initialization is under
 // consideration here: https://github.com/w3c/csswg-drafts/issues/2075.
+//
+// TODO(https://crbug.com/986925): The playback rate should be taken into
+// consideration when calculating the initial current time.
+// https://drafts.csswg.org/web-animations/#playing-an-animation-section
 base::Optional<base::TimeDelta> WorkletAnimation::InitialCurrentTime() const {
   if (play_state_ == Animation::kIdle || play_state_ == Animation::kUnset ||
       !IsTimelineActive())
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index c0ef014..46cbf4e 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -513,7 +513,7 @@
       // https://crbug.com/879270
       name: "ElementTiming",
       origin_trial_feature_name: "ElementTimingImages",
-      status: "experimental",
+      status: "stable",
     },
     {
       name: "EncryptedMediaEncryptionSchemeQuery",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 34eae48..888891d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3262,14 +3262,12 @@
 
 # ====== New tests from wpt-importer added here ======
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-thickness-overline-001.html [ Failure ]
-crbug.com/626703 external/wpt/webxr/xrWebGLLayer_viewports.https.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-thickness-vertical-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-thickness-scroll-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-thickness-linethrough-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/white-space/break-spaces-tab-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-thickness-underline-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-thickness-vertical-002.html [ Failure ]
-crbug.com/626703 external/wpt/webxr/xrWebGLLayer_framebuffer.https.html [ Timeout ]
 crbug.com/626703 external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.https.html [ Timeout ]
 crbug.com/626703 [ Win10 ] virtual/omt-worker-fetch/external/wpt/fetch/api/request/destination/fetch-destination-worker.https.html [ Crash ]
 crbug.com/626703 external/wpt/webauthn/idlharness-manual.https.window.js [ Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/supported-lcp-type.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/supported-lcp-type.html
new file mode 100644
index 0000000..25d4eaa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/supported-lcp-type.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<head>
+<title>PerformanceObserver.supportedEntryTypes contains "largest-contentful-paint"</title>
+</head>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+  if (typeof PerformanceObserver.supportedEntryTypes === "undefined")
+    assert_unreached("supportedEntryTypes is not supported.");
+  assert_greater_than(PerformanceObserver.supportedEntryTypes.indexOf("largest-contentful-paint"), -1,
+    "There should be an entry 'largest-contentful-paint' in PerformanceObserver.supportedEntryTypes");
+}, "supportedEntryTypes contains 'largest-contentful-paint'.");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/supported-layout-type.html b/third_party/blink/web_tests/external/wpt/layout-instability/supported-layout-type.html
index 9bc5370..8679a2d 100644
--- a/third_party/blink/web_tests/external/wpt/layout-instability/supported-layout-type.html
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/supported-layout-type.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
 <head>
-<title>PerformanceObserver.supportedEntryTypes contains "layoutShift"</title>
+<title>PerformanceObserver.supportedEntryTypes contains "layout-shift"</title>
 </head>
 <body>
 <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/attributes.html b/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
index 6b6aca624..9b87280b 100644
--- a/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
+++ b/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
@@ -100,11 +100,6 @@
     assertToastNotShown(toast);
 }, 'setting `toast.open` to some falsy value on a shown toast will hide the toast');
 
-testToastElement((toast) => {
-    toast.toggleAttribute('open');
-    assert_true(toast.open);
-}, 'toggling `open` on a hidden toast sets `toast.open` to true');
-
 testToastElementAsync((t, toast) => {
     toast.toggleAttribute('open', true);
 
@@ -125,36 +120,6 @@
 }, 'default type is empty string without attribute present');
 
 testToastElement((toast) => {
-    toast.type = 'warning';
-    assert_equals(toast.getAttribute('type'), 'warning');
-    assert_equals(toast.type, 'warning');
-}, 'setting type property to an enumerated value changes the type attribute to that value');
-
-testToastElement((toast) => {
-    toast.type = 'WaRnInG';
-    assert_equals(toast.getAttribute('type'), 'WaRnInG');
-    assert_equals(toast.type, 'warning');
-}, 'setting type property to an enumerated value is case-insensitive');
-
-testToastElement((toast) => {
-    toast.type = '  WaRnInG ';
-    assert_equals(toast.getAttribute('type'), '  WaRnInG ');
-    assert_equals(toast.type, '');
-}, 'setting type property to an enumerated value with whitespace does not work');
-
-testToastElement((toast) => {
-    toast.type = 'test';
-    assert_equals(toast.type, '');
-    assert_equals(toast.getAttribute('type'), 'test');
-}, 'setting type to a non-enumerated value sets the type property to empty string');
-
-testToastElement((toast) => {
-    toast.setAttribute('type', 'test');
-    assert_equals(toast.type, '');
-    assert_equals(toast.getAttribute('type'), 'test');
-}, 'setting type attribute to a non-enumerated value sets the type property to empty string');
-
-testToastElement((toast) => {
     toast.type = 'info';
     assert_equals(toast.type, '');
     assert_equals(toast.getAttribute('type'), 'info');
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/options.html b/third_party/blink/web_tests/external/wpt/std-toast/options.html
index 6d4a462..b75ff72c 100644
--- a/third_party/blink/web_tests/external/wpt/std-toast/options.html
+++ b/third_party/blink/web_tests/external/wpt/std-toast/options.html
@@ -101,4 +101,23 @@
         toast.remove();
     });
 }, 'showToast closes after user specified 50ms');
+
+// type
+test(() => {
+    const toast = showToast('Message', {type: 'warning'});
+    assert_equals(toast.type, 'warning');
+    assert_equals(toast.getAttribute('type'), 'warning');
+}, 'passing a valid type option to showToast sets the type property and attribute');
+
+test(() => {
+    const toast = showToast('Message', {type: 'test'});
+    assert_equals(toast.type, '');
+    assert_equals(toast.getAttribute('type'), 'test');
+}, 'passing an invalid type option to showToast sets the type attribute, but not the property');
+
+test(() => {
+    const toast = showToast('Message');
+    assert_equals(toast.type, '');
+    assert_false(toast.hasAttribute('type'));
+}, 'passing nothing as the type option to showToast does not set the type attribute or property');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/reflection.html b/third_party/blink/web_tests/external/wpt/std-toast/reflection.html
new file mode 100644
index 0000000..36cbaf6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/std-toast/reflection.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>HTML5 reflection tests:  std-toast</title>
+<meta name=timeout content=long>
+
+<div id=log></div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/html/dom/original-harness.js"></script>
+<script src="/html/dom/new-harness.js"></script>
+
+<script type="module">
+import 'std:elements/toast';
+
+const toastElement = {
+    'std-toast': {
+        open: 'boolean',
+        type: {type: 'enum', keywords: ['success', 'error', 'warning']},
+    },
+};
+
+mergeElements(toastElement);
+</script>
+<script src="/html/dom/reflection.js" defer></script>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_asserts.js b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_asserts.js
index 36524211..24b5274b 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_asserts.js
+++ b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_asserts.js
@@ -6,19 +6,107 @@
 // |epsilon| - float specifying precision
 // |prefix| - string used as a prefix for logging
 let assert_point_approx_equals = function(p1, p2, epsilon = FLOAT_EPSILON, prefix = "") {
-  assert_approx_equals(p1.x, p2.x, epsilon, prefix + "xs must match");
-  assert_approx_equals(p1.y, p2.y, epsilon, prefix + "ys must match");
-  assert_approx_equals(p1.z, p2.z, epsilon, prefix + "zs must match");
-  assert_approx_equals(p1.w, p2.w, epsilon, prefix + "ws must match");
+  if (p1 == null && p2 == null) {
+    return;
+  }
+
+  assert_not_equals(p1, null, prefix + "p1 must be non-null");
+  assert_not_equals(p2, null, prefix + "p2 must be non-null");
+
+  let mismatched_component = null;
+  for (const v of ['x', 'y', 'z', 'w']) {
+    if (Math.abs(p1[v] - p2[v]) > epsilon) {
+      mismatched_component = v;
+      break;
+    }
+  }
+
+  if (mismatched_component !== null) {
+    let error_message = prefix + ' Point comparison failed.\n';
+    error_message += ` p1: {x: ${p1.x}, y: ${p1.y}, z: ${p1.z}, w: ${p1.w}}\n`;
+    error_message += ` p2: {x: ${p2.x}, y: ${p2.y}, z: ${p2.z}, w: ${p2.w}}\n`;
+    error_message += ` Difference in component ${mismatched_component} exceeded the given epsilon.\n`;
+    assert_approx_equals(p2[mismatched_component], p1[mismatched_component], epsilon, error_message);
+  }
 };
 
 // |m1|, |m2| - arrays of floating point numbers
 // |epsilon| - float specifying precision
 // |prefix| - string used as a prefix for logging
 let assert_matrix_approx_equals = function(m1, m2, epsilon = FLOAT_EPSILON, prefix = "") {
-  assert_equals(m1.length, m2.length, prefix + "Matrix lengths should match");
-  for(var i = 0; i < m1.length; ++i) {
-    assert_approx_equals(m1[i], m2[i], epsilon, prefix + "Component number " + i + " should match");
+  if (m1 == null && m2 == null) {
+    return;
+  }
+
+  assert_not_equals(m1, null, prefix + "m1 must be non-null");
+  assert_not_equals(m2, null, prefix + "m2 must be non-null");
+
+  assert_equals(m1.length, 16, prefix + "m1 must have length of 16");
+  assert_equals(m2.length, 16, prefix + "m2 must have length of 16");
+
+  let mismatched_element = -1;
+  for (let i = 0; i < 16; ++i) {
+    if (Math.abs(m1[i] - m2[i]) > epsilon) {
+      mismatched_element = i;
+      break;
+    }
+  }
+
+  if (mismatched_element > -1) {
+    let error_message = prefix + 'Matrix comparison failed.\n';
+    error_message += ' Difference in element ' + mismatched_element +
+        ' exceeded the given epsilon.\n';
+
+    error_message += ' Matrix 1: [' + m1.join(',') + ']\n';
+    error_message += ' Matrix 2: [' + m2.join(',') + ']\n';
+
+    assert_approx_equals(
+        m1[mismatched_element], m2[mismatched_element], epsilon,
+        error_message);
+  }
+}
+
+
+// |m1|, |m2| - arrays of floating point numbers
+// |epsilon| - float specifying precision
+// |prefix| - string used as a prefix for logging
+let assert_matrix_significantly_not_equals = function(m1, m2, epsilon = FLOAT_EPSILON, prefix = "") {
+  if (m1 == null && m2 == null) {
+    return;
+  }
+
+  assert_not_equals(m1, null, prefix + "m1 must be non-null");
+  assert_not_equals(m2, null, prefix + "m2 must be non-null");
+
+  assert_equals(m1.length, 16, prefix + "m1 must have length of 16");
+  assert_equals(m2.length, 16, prefix + "m2 must have length of 16");
+
+  let mismatch = false;
+  for (let i = 0; i < 16; ++i) {
+    if (Math.abs(m1[i] - m2[i]) > epsilon) {
+      mismatch = true;
+      break;
+    }
+  }
+
+  if (!mismatch) {
+    let m1_str = '[';
+    let m2_str = '[';
+    for (let i = 0; i < 16; ++i) {
+      m1_str += m1[i] + (i < 15 ? ', ' : '');
+      m2_str += m2[i] + (i < 15 ? ', ' : '');
+    }
+    m1_str += ']';
+    m2_str += ']';
+
+    let error_message = prefix + 'Matrix comparison failed.\n';
+    error_message +=
+        ' No element exceeded the given epsilon ' + epsilon + '.\n';
+
+    error_message += ' Matrix A: ' + m1_str + '\n';
+    error_message += ' Matrix B: ' + m2_str + '\n';
+
+    assert_unreached(error_message);
   }
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https-expected.txt
new file mode 100644
index 0000000..54e5aa4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL XRWebGLLayer reports a valid framebuffer for immersive sessions assert_equals: expected (number) 5890 but got (object) null
+FAIL XRWebGLLayer reports a valid framebuffer for inline sessions assert_not_equals: got disallowed value null
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https.html
index 7c4ff17..ba6b7dc 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_framebuffer.https.html
@@ -20,26 +20,37 @@
       let gl = layer.context;
 
       // The layer's framebuffer is a WebGL framebuffer
-      assert_not_equals(layer.framebuffer, null);
-      assert_true(layer.framebuffer instanceof WebGLFramebuffer);
+      t.step(() => {
+        assert_not_equals(layer.framebuffer, null);
+        assert_true(layer.framebuffer instanceof WebGLFramebuffer);
 
-      // The XR framebuffer is not bound to the GL context by default.
-      assert_not_equals(layer.framebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));
+        // The XR framebuffer is not bound to the GL context by default.
+        assert_not_equals(layer.framebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));
+      });
 
       // The XR framebuffer can be bound to the GL context.
       gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
-      assert_equals(layer.framebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));
+
+      t.step(() => {
+        assert_equals(layer.framebuffer, gl.getParameter(gl.FRAMEBUFFER_BINDING));
+      });
 
       // The XR framebuffer has a 2D texture
       let attachment = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
-      assert_equals(attachment, gl.TEXTURE);
+
+      t.step(() => {
+        assert_equals(attachment, gl.TEXTURE);
+      });
 
       // Check that each viewport fits inside the framebuffer dimensions
       let viewer_pose = xrFrame.getViewerPose(space);
       for (view of viewer_pose.views) {
         let viewport = layer.getViewport(view);
-        assert_less_than_equal(viewport.x + viewport.width, layer.framebufferWidth);
-        assert_less_than_equal(viewport.y + viewport.height, layer.framebufferHeight);
+
+        t.step(() => {
+          assert_less_than_equal(viewport.x + viewport.width, layer.framebufferWidth);
+          assert_less_than_equal(viewport.y + viewport.height, layer.framebufferHeight);
+        });
       }
 
       // Finished test.
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_viewports.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_viewports.https.html
index 8cfccff..94a23dc 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_viewports.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_viewports.https.html
@@ -22,18 +22,18 @@
       let layer = xrFrame.session.renderState.baseLayer;
       for (view of viewer_pose.views) {
         let viewport = layer.getViewport(view);
-        let index = (view.eye === "right"? 1: 0);
-        let params = fakeDeviceInitParams.views[index];
 
-        // Ensure the returned object is an XRViewport object
-        assert_not_equals(viewport, null);
-        assert_true(viewport instanceof XRViewport);
+        t.step(() => {
+          // Ensure the returned object is an XRViewport object
+          assert_not_equals(viewport, null);
+          assert_true(viewport instanceof XRViewport);
 
-        // Ensure the viewport dimensions are valid
-        assert_greater_than_equal(viewport.x, 0);
-        assert_greater_than_equal(viewport.y, 0);
-        assert_equals(viewport.width, params.resolution.width);
-        assert_equals(viewport.height, params.resolution.height);
+          // Ensure the viewport dimensions are valid
+          assert_greater_than_equal(viewport.x, 0);
+          assert_greater_than_equal(viewport.y, 0);
+          assert_greater_than_equal(viewport.width, 1);
+          assert_greater_than_equal(viewport.height, 1);
+        });
 
         // Ensure none of the viewports overlap
         for (other of viewer_pose.views) {
@@ -43,7 +43,10 @@
               (otherport.x + otherport.width <= viewport.x) ||
               (viewport.y + viewport.height <= otherport.y) ||
               (otherport.y + otherport.height <= viewport.y);
-            assert_true(no_overlap, "Overlap between viewport " + view.eye + " and " + other.eye);
+
+            t.step(() => {
+              assert_true(no_overlap, "Overlap between viewport " + view.eye + " and " + other.eye);
+            });
           }
         }
       }
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/dom-breakpoints-pane-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/dom-breakpoints-pane-a11y-test-expected.txt
new file mode 100644
index 0000000..8134345
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/dom-breakpoints-pane-a11y-test-expected.txt
@@ -0,0 +1,7 @@
+Testing accessibility in the DOM breakpoints pane.
+Setting DOM breakpoints.
+DOM breakpoints pane text content: Subtree modifiedNode removedNo breakpoints
+Running the axe-core linter on the DOM breakpoints pane.
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/dom-breakpoints-pane-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/dom-breakpoints-pane-a11y-test.js
new file mode 100644
index 0000000..babf0c1b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/dom-breakpoints-pane-a11y-test.js
@@ -0,0 +1,33 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('sources');
+  await TestRunner.navigatePromise(
+      '../../sources/debugger-breakpoints/resources/dom-breakpoints.html');
+
+  TestRunner.addResult('Testing accessibility in the DOM breakpoints pane.');
+  await UI.viewManager.showView('sources.domBreakpoints');
+  const domBreakpointsPane =
+      self.runtime.sharedInstance(BrowserDebugger.DOMBreakpointsSidebarPane);
+
+  TestRunner.addResult('Setting DOM breakpoints.');
+  const rootElement = await ElementsTestRunner.nodeWithIdPromise('rootElement');
+  TestRunner.domDebuggerModel.setDOMBreakpoint(
+      rootElement, SDK.DOMDebuggerModel.DOMBreakpoint.Type.SubtreeModified);
+
+  const hostElement = await ElementsTestRunner.nodeWithIdPromise('hostElement');
+  const breakpoint = TestRunner.domDebuggerModel.setDOMBreakpoint(
+      hostElement, SDK.DOMDebuggerModel.DOMBreakpoint.Type.NodeRemoved);
+  TestRunner.domDebuggerModel.toggleDOMBreakpoint(breakpoint, false);
+
+  TestRunner.addResult(`DOM breakpoints pane text content: ${domBreakpointsPane.contentElement.deepTextContent()}`);
+  TestRunner.addResult(
+      'Running the axe-core linter on the DOM breakpoints pane.');
+  await AxeCoreTestRunner.runValidation(domBreakpointsPane.contentElement);
+
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-navigation-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-navigation-expected.txt
new file mode 100644
index 0000000..36aaf981
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-navigation-expected.txt
@@ -0,0 +1,6 @@
+Verifies that navigation requests have their raw headers shown in Network.*ExtraInfo events by checking cookie headers.
+
+Response set-cookie header: name=value; Secure; SameSite=None; HttpOnly
+
+Request cookie header: name=value
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-navigation.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-navigation.js
new file mode 100644
index 0000000..78134a8
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-navigation.js
@@ -0,0 +1,25 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Verifies that navigation requests have their raw headers shown in Network.*ExtraInfo events by checking cookie headers.\n`);
+
+  await dp.Network.enable();
+
+  const setCookieUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/set-cookie-samesitenone.php';
+  const helloWorldUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/hello-world.html';
+  const otherDomainUrl = 'https://thirdparty.test:8443/inspector-protocol/network/resources/hello-world.html';
+
+  const helper = (await testRunner.loadScript('resources/extra-info-helper.js'))(dp, session);
+
+  // set the cookie with a navigation request
+  const {responseExtraInfo} = await helper.navigateWithExtraInfo(setCookieUrl);
+  testRunner.log(`Response set-cookie header: ${responseExtraInfo.params.headers['set-cookie']}\n`);
+
+  // navigate to a different domain
+  await helper.navigateWithExtraInfo(otherDomainUrl);
+
+  // make a cross origin navigation request to the domain with the cookie
+  const {requestExtraInfo} = await helper.navigateWithExtraInfo(helloWorldUrl);
+  testRunner.log(`Request cookie header: ${requestExtraInfo.params.headers['Cookie']}`);
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-subresource-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-subresource-expected.txt
new file mode 100644
index 0000000..f8125e686
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-subresource-expected.txt
@@ -0,0 +1,6 @@
+Verifies that cross origin requests have their raw headers shown in Network.*ExtraInfo events by checking cookie headers.
+
+Response set-cookie header: name=value; Secure; SameSite=None; HttpOnly
+
+Request cookie header: name=value
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-subresource.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-subresource.js
new file mode 100644
index 0000000..5913bc6
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-raw-headers-subresource.js
@@ -0,0 +1,25 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Verifies that cross origin requests have their raw headers shown in Network.*ExtraInfo events by checking cookie headers.\n`);
+
+  await dp.Network.enable();
+
+  const setCookieUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/set-cookie-samesitenone.php';
+  const firstPartyUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/hello-world.html';
+  const thirdPartyUrl = 'https://thirdparty.test:8443/inspector-protocol/network/resources/hello-world.html';
+
+  const helper = (await testRunner.loadScript('resources/extra-info-helper.js'))(dp, session);
+
+  // navigate to a third party domain
+  await helper.navigateWithExtraInfo(thirdPartyUrl);
+
+  // set the cookie with a subresource request
+  const {responseExtraInfo} = await helper.fetchWithExtraInfo(setCookieUrl);
+  testRunner.log(`Response set-cookie header: ${responseExtraInfo.params.headers['set-cookie']}\n`);
+
+  // make a cross origin subresource request to the domain with the cookie
+  const {requestExtraInfo} = await helper.fetchWithExtraInfo(firstPartyUrl);
+  testRunner.log(`Request cookie header: ${requestExtraInfo.params.headers['Cookie']}`);
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/extra-info-helper.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/extra-info-helper.js
new file mode 100644
index 0000000..1e5b3ec
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/extra-info-helper.js
@@ -0,0 +1,30 @@
+(function() {
+  class FetchExtraInfoHelper {
+    constructor(dp, session) {
+      this._dp = dp;
+      this._session = session;
+    }
+
+    async navigateWithExtraInfo(url) {
+      const requestExtraInfoPromise = this._dp.Network.onceRequestWillBeSentExtraInfo();
+      const responseExtraInfoPromise = this._dp.Network.onceResponseReceivedExtraInfo();
+      await this._session.navigate(url);
+      const requestExtraInfo = await requestExtraInfoPromise;
+      const responseExtraInfo = await responseExtraInfoPromise;
+      return {requestExtraInfo, responseExtraInfo};
+    }
+
+    async fetchWithExtraInfo(url) {
+      const requestExtraInfoPromise = this._dp.Network.onceRequestWillBeSentExtraInfo();
+      const responseExtraInfoPromise = this._dp.Network.onceResponseReceivedExtraInfo();
+      await this._session.evaluate(`fetch('${url}', {method: 'POST', credentials: 'include'})`);
+      const requestExtraInfo = await requestExtraInfoPromise;
+      const responseExtraInfo = await responseExtraInfoPromise;
+      return {requestExtraInfo, responseExtraInfo};
+    }
+  };
+
+  return (dp, session) => {
+    return new FetchExtraInfoHelper(dp, session);
+  };
+})()
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/hello-world.html b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/hello-world.html
new file mode 100644
index 0000000..14046a9b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/hello-world.html
@@ -0,0 +1,3 @@
+<body>
+  <h1>hello world</h1>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/set-cookie-samesitenone.php b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/set-cookie-samesitenone.php
new file mode 100644
index 0000000..6500c19
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/set-cookie-samesitenone.php
@@ -0,0 +1,3 @@
+<?php
+header('set-cookie: name=value; Secure; SameSite=None; HttpOnly')
+?>
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index b259fa20..3651da85 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4657,6 +4657,19 @@
     method setResourceTimingBufferSize
     method toJSON
     setter onresourcetimingbufferfull
+interface PerformanceElementTiming : PerformanceEntry
+    attribute @@toStringTag
+    getter element
+    getter id
+    getter identifier
+    getter intersectionRect
+    getter loadTime
+    getter naturalHeight
+    getter naturalWidth
+    getter renderTime
+    getter url
+    method constructor
+    method toJSON
 interface PerformanceEntry
     attribute @@toStringTag
     getter duration
diff --git a/third_party/blink/web_tests/xr/ar_hittest.html b/third_party/blink/web_tests/xr/ar_hittest.html
index be8d40b..d222715 100644
--- a/third_party/blink/web_tests/xr/ar_hittest.html
+++ b/third_party/blink/web_tests/xr/ar_hittest.html
@@ -7,6 +7,7 @@
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
+<script src="../external/wpt/webxr/resources/webxr_test_asserts.js"></script>
 <script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas />
@@ -42,7 +43,7 @@
             // Test that hit results are what we expected.
             assert_equals(hitResults.length, 1);
             assert_equals(hitResults[0].hitMatrix.length, 16);
-            assert_matrices_approx_equal(
+            assert_matrix_approx_equals(
               hitResults[0].hitMatrix,
               expectedHitMatrix,
               FLOAT_EPSILON,
diff --git a/third_party/blink/web_tests/xr/resources/xr-test-utils.js b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
index c0abd4be..46026e7 100644
--- a/third_party/blink/web_tests/xr/resources/xr-test-utils.js
+++ b/third_party/blink/web_tests/xr/resources/xr-test-utils.js
@@ -75,136 +75,3 @@
         }));
   }, name, properties);
 }
-
-function perspectiveFromFieldOfView(fov, near, far) {
-  let upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
-  let downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
-  let leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
-  let rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
-  let xScale = 2.0 / (leftTan + rightTan);
-  let yScale = 2.0 / (upTan + downTan);
-  let nf = 1.0 / (near - far);
-
-  let out = new Float32Array(16);
-  out[0] = xScale;
-  out[1] = 0.0;
-  out[2] = 0.0;
-  out[3] = 0.0;
-  out[4] = 0.0;
-  out[5] = yScale;
-  out[6] = 0.0;
-  out[7] = 0.0;
-  out[8] = -((leftTan - rightTan) * xScale * 0.5);
-  out[9] = ((upTan - downTan) * yScale * 0.5);
-  out[10] = (near + far) * nf;
-  out[11] = -1.0;
-  out[12] = 0.0;
-  out[13] = 0.0;
-  out[14] = (2.0 * far * near) * nf;
-  out[15] = 0.0;
-
-  return out;
-}
-
-function assert_points_approx_equal(actual, expected, epsilon = FLOAT_EPSILON, message = '') {
-  if (expected == null && actual == null) {
-    return;
-  }
-
-  assert_not_equals(expected, null);
-  assert_not_equals(actual, null);
-
-  let mismatched_component = null;
-  for (const v of ['x', 'y', 'z', 'w']) {
-    if (Math.abs(expected[v] - actual[v]) > epsilon) {
-      mismatched_component = v;
-      break;
-    }
-  }
-
-  if (mismatched_component !== null) {
-    let error_message = message ? message + '\n' : 'Point comparison failed.\n';
-    error_message += ` Expected: {x: ${expected.x}, y: ${expected.y}, z: ${expected.z}, w: ${expected.w}}\n`;
-    error_message += ` Actual: {x: ${actual.x}, y: ${actual.y}, z: ${actual.z}, w: ${actual.w}}\n`;
-    error_message += ` Difference in component ${mismatched_component} exceeded the given epsilon.\n`;
-    assert_approx_equals(actual[mismatched_component], expected[mismatched_component], epsilon, error_message);
-  }
-}
-
-function assert_matrices_approx_equal(
-    matA, matB, epsilon = FLOAT_EPSILON, message = '') {
-  if (matA == null && matB == null) {
-    return;
-  }
-
-  assert_not_equals(matA, null);
-  assert_not_equals(matB, null);
-
-  assert_equals(matA.length, 16);
-  assert_equals(matB.length, 16);
-
-  let mismatched_element = -1;
-  for (let i = 0; i < 16; ++i) {
-    if (Math.abs(matA[i] - matB[i]) > epsilon) {
-      mismatched_element = i;
-      break;
-    }
-  }
-
-  if (mismatched_element > -1) {
-    let error_message =
-        message ? message + '\n' : 'Matrix comparison failed.\n';
-    error_message += ' Difference in element ' + mismatched_element +
-        ' exceeded the given epsilon.\n';
-
-    error_message += ' Matrix A: [' + matA.join(',') + ']\n';
-    error_message += ' Matrix B: [' + matB.join(',') + ']\n';
-
-    assert_approx_equals(
-        matA[mismatched_element], matB[mismatched_element], epsilon,
-        error_message);
-  }
-}
-
-
-function assert_matrices_significantly_not_equal(
-    matA, matB, epsilon = FLOAT_EPSILON, message = '') {
-  if (matA == null && matB == null) {
-    return;
-  }
-
-  assert_not_equals(matA, null);
-  assert_not_equals(matB, null);
-
-  assert_equals(matA.length, 16);
-  assert_equals(matB.length, 16);
-
-  let mismatch = false;
-  for (let i = 0; i < 16; ++i) {
-    if (Math.abs(matA[i] - matB[i]) > epsilon) {
-      mismatch = true;
-      break;
-    }
-  }
-
-  if (!mismatch) {
-    let matA_str = '[';
-    let matB_str = '[';
-    for (let i = 0; i < 16; ++i) {
-      matA_str += matA[i] + (i < 15 ? ', ' : '');
-      matB_str += matB[i] + (i < 15 ? ', ' : '');
-    }
-    matA_str += ']';
-    matB_str += ']';
-
-    let error_message =
-        message ? message + '\n' : 'Matrix comparison failed.\n';
-    error_message +=
-        ' No element exceeded the given epsilon ' + epsilon + '.\n';
-
-    error_message += ' Matrix A: ' + matA_str + '\n';
-    error_message += ' Matrix B: ' + matB_str + '\n';
-
-    assert_unreached(error_message);
-  }
-}
diff --git a/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html b/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html
index 2fff790..7f02453 100644
--- a/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html
+++ b/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html
@@ -7,6 +7,7 @@
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
+<script src="../external/wpt/webxr/resources/webxr_test_asserts.js"></script>
 <script src="../xr/resources/xr-internal-device-mocking.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas />
@@ -41,7 +42,7 @@
               assert_not_equals(pose, null);
 
               let poseMatrix = pose.transform.matrix;
-              assert_matrices_approx_equal(poseMatrix, VALID_FLOOR_ORIGIN_MATRIX);
+              assert_matrix_approx_equals(poseMatrix, VALID_FLOOR_ORIGIN_MATRIX);
 
               // If an explicit array of bounds points was not provided then the
               // bounds geometry should represent the four corners of the rectangle
diff --git a/third_party/blink/web_tests/xr/xrView_match.html b/third_party/blink/web_tests/xr/xrView_match.html
index ea0d1ce..a6f4979 100644
--- a/third_party/blink/web_tests/xr/xrView_match.html
+++ b/third_party/blink/web_tests/xr/xrView_match.html
@@ -7,6 +7,7 @@
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
+<script src="../external/wpt/webxr/resources/webxr_test_asserts.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas />
 
@@ -53,7 +54,7 @@
           assert_false(pose2 instanceof XRViewerPose);
           assert_not_equals(pose2.transform, null);
           assert_not_equals(pose2.transform.matrix, null);
-          assert_matrices_approx_equal(pose.transform.matrix, pose2.transform.matrix);
+          assert_matrix_approx_equals(pose.transform.matrix, pose2.transform.matrix);
         }
 
         // Ensure that two views are provided.
@@ -72,8 +73,8 @@
         assert_not_equals(leftView.projectionMatrix, null);
         assert_not_equals(rightView.projectionMatrix, null);
 
-        assert_matrices_approx_equal(leftView.projectionMatrix, VALID_PROJECTION_MATRIX);
-        assert_matrices_approx_equal(rightView.projectionMatrix, VALID_PROJECTION_MATRIX);
+        assert_matrix_approx_equals(leftView.projectionMatrix, VALID_PROJECTION_MATRIX);
+        assert_matrix_approx_equals(rightView.projectionMatrix, VALID_PROJECTION_MATRIX);
 
         // Finished test.
         resolve();
diff --git a/third_party/blink/web_tests/xr/xrView_oneframeupdate.html b/third_party/blink/web_tests/xr/xrView_oneframeupdate.html
index 6364fe3..3b02fca 100644
--- a/third_party/blink/web_tests/xr/xrView_oneframeupdate.html
+++ b/third_party/blink/web_tests/xr/xrView_oneframeupdate.html
@@ -7,6 +7,7 @@
 <script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
 <script src="../external/wpt/resources/chromium/webxr-test.js"></script>
 <script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
+<script src="../external/wpt/webxr/resources/webxr_test_asserts.js"></script>
 <script src="../xr/resources/xr-test-utils.js"></script>
 <canvas />
 
@@ -47,8 +48,8 @@
         if (counter == 0) {
           session.requestAnimationFrame(onFrame);
 
-          assert_matrices_approx_equal(pose.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
-          assert_matrices_approx_equal(pose.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrix_approx_equals(pose.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrix_approx_equals(pose.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
 
           // Update the near and far depths for the session.
           session.updateRenderState({
@@ -57,8 +58,8 @@
 
           // The projection matrices the views report should not take into
           // account the new session depth values this frame.
-          assert_matrices_approx_equal(pose.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
-          assert_matrices_approx_equal(pose.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrix_approx_equals(pose.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrix_approx_equals(pose.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
         } else {
           // New depth values should be retained between frames.
           assert_equals(session.renderState.depthNear, 1.0);
@@ -66,8 +67,8 @@
 
           // Projection matricies should now reflect the new depth values, i.e.
           // have changed.
-          assert_matrices_significantly_not_equal(pose.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
-          assert_matrices_significantly_not_equal(pose.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrix_significantly_not_equals(pose.views[0].projectionMatrix, VALID_PROJECTION_MATRIX);
+          assert_matrix_significantly_not_equals(pose.views[1].projectionMatrix, VALID_PROJECTION_MATRIX);
           resolve();
         }
         counter++;
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/color.js b/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
index 0105d3a5..6af2fa3 100644
--- a/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
+++ b/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
@@ -19,30 +19,50 @@
 
       /* Material Design color palette for Google products */
 
-      --google-red-100: #f4c7c3;
-      --google-red-300: #e67c73;
-      --google-red-500: #db4437;
-      --google-red-700: #c53929;
+      --google-red-100-rgb: 244, 199, 195;  /* #f4c7c3 */
+      --google-red-100: rgb(var(--google-red-100-rgb));
+      --google-red-300-rgb: 230, 124, 115;  /* #e67c73 */
+      --google-red-300: rgb(var(--google-red-300-rgb));
+      --google-red-500-rgb: 219, 68, 55;  /* #db4437 */
+      --google-red-500: rgb(var(--google-red-500-rgb));
+      --google-red-700-rgb: 197, 57, 41;  /* #c53929 */
+      --google-red-700: rgb(var(--google-red-700-rgb));
 
-      --google-blue-100: #c6dafc;
-      --google-blue-300: #7baaf7;
-      --google-blue-500: #4285f4;
-      --google-blue-700: #3367d6;
+      --google-blue-100-rgb: 198, 218, 252;  /* #c6dafc */
+      --google-blue-100: rgb(var(--google-blue-100-rgb));
+      --google-blue-300-rgb: 123, 170, 247;  /* #7baaf7 */
+      --google-blue-300: rgb(var(--google-blue-300-rgb));
+      --google-blue-500-rgb: 66, 133, 244;  /* #4285f4 */
+      --google-blue-500: rgb(var(--google-blue-500-rgb));
+      --google-blue-700-rgb: 51, 103, 214;  /* #3367d6 */
+      --google-blue-700: rgb(var(--google-blue-700-rgb));
 
-      --google-green-100: #b7e1cd;
-      --google-green-300: #57bb8a;
-      --google-green-500: #0f9d58;
-      --google-green-700: #0b8043;
+      --google-green-100-rgb: 183, 225, 205;  /* #b7e1cd */
+      --google-green-100: rgb(var(--google-green-100-rgb));
+      --google-green-300-rgb: 87, 187, 138;  /* #57bb8a */
+      --google-green-300: rgb(var(--google-green-300-rgb));
+      --google-green-500-rgb: 15, 157, 88;  /* #0f9d58 */
+      --google-green-500: rgb(var(--google-green-500-rgb));
+      --google-green-700-rgb: 11, 128, 67;  /* #0b8043 */
+      --google-green-700: rgb(var(--google-green-700-rgb));
 
-      --google-yellow-100: #fce8b2;
-      --google-yellow-300: #f7cb4d;
-      --google-yellow-500: #f4b400;
-      --google-yellow-700: #f09300;
+      --google-yellow-100-rgb: 252, 232, 178;  /* #fce8b2 */
+      --google-yellow-100: rgb(var(--google-yellow-100-rgb));
+      --google-yellow-300-rgb: 247, 203, 77;  /* #f7cb4d */
+      --google-yellow-300: rgb(var(--google-yellow-300-rgb));
+      --google-yellow-500-rgb: 244, 180, 0;  /* #f4b400 */
+      --google-yellow-500: rgb(var(--google-yellow-500-rgb));
+      --google-yellow-700-rgb: 240, 147, 0;  /* #f09300 */
+      --google-yellow-700: rgb(var(--google-yellow-700-rgb));
 
-      --google-grey-100: #f5f5f5;
-      --google-grey-300: #e0e0e0;
-      --google-grey-500: #9e9e9e;
-      --google-grey-700: #616161;
+      --google-grey-100-rgb: 245, 245, 245;  /* #f5f5f5 */
+      --google-grey-100: rgb(var(--google-grey-100-rgb));
+      --google-grey-300-rgb: 224, 224, 224;  /* #e0e0e0 */
+      --google-grey-300: rgb(var(--google-grey-300-rgb));
+      --google-grey-500-rgb: 158, 158, 158;  /* #9e9e9e */
+      --google-grey-500: rgb(var(--google-grey-500-rgb));
+      --google-grey-700-rgb: 97, 97, 97;  /* #616161 */
+      --google-grey-700: rgb(var(--google-grey-700-rgb));
 
       /* Material Design color palette from online spec document */
 
@@ -337,4 +357,4 @@
 </custom-style>
 `;
 template.setAttribute('style', 'display: none;');
-document.head.appendChild(template.content);
+document.head.appendChild(template.content);
\ No newline at end of file
diff --git a/third_party/polymer/v3_0/reproduce.sh b/third_party/polymer/v3_0/reproduce.sh
index c3b4ef499..d75b595 100755
--- a/third_party/polymer/v3_0/reproduce.sh
+++ b/third_party/polymer/v3_0/reproduce.sh
@@ -85,7 +85,10 @@
 echo 'Stripping unnecessary prefixed CSS rules...'
 python ../v1_0/css_strip_prefixes.py --file_extension=js
 
-# TODO rgbify_hex_vars.py
+echo 'Generating -rgb versions of --google-* vars in paper-style/colors.js...'
+python ../v1_0/rgbify_hex_vars.py --filter-prefix=google --replace \
+    components-chromium/paper-styles/color.js
+
 # TODO create components summary
 # TODO generate gn
 # TODO find unused elements?
diff --git a/tools/binary_size/libsupersize/archive.py b/tools/binary_size/libsupersize/archive.py
index e518dd5..8205bbb 100644
--- a/tools/binary_size/libsupersize/archive.py
+++ b/tools/binary_size/libsupersize/archive.py
@@ -1339,6 +1339,18 @@
     logging.info('Parsing ninja files.')
     source_mapper, ninja_elf_object_paths = (
         ninja_parser.Parse(output_directory, elf_path))
+
+    # If no symbols came from the library, it's because it's a partition
+    # extracted from a combined library. Look there instead.
+    if not ninja_elf_object_paths and elf_path:
+      combined_elf_path = elf_path.replace('.so', '__combined.so')
+      logging.info('Found no objects in %s, trying %s', elf_path,
+                   combined_elf_path)
+      source_mapper, ninja_elf_object_paths = (ninja_parser.Parse(
+          output_directory, combined_elf_path))
+      if ninja_elf_object_paths:
+        assert map_path and '__combined.so.map' in map_path
+
     logging.debug('Parsed %d .ninja files.', source_mapper.parsed_file_count)
     assert not elf_path or ninja_elf_object_paths, (
         'Failed to find link command in ninja files for ' +
@@ -1487,6 +1499,11 @@
   return section_sizes
 
 
+def _ElfIsMainPartition(elf_path, tool_prefix):
+  section_names = _SectionSizesFromElf(elf_path, tool_prefix).keys()
+  return models.SECTION_PART_END in section_names
+
+
 def _ArchFromElf(elf_path, tool_prefix):
   args = [path_util.GetReadElfPath(tool_prefix), '-h', elf_path]
   stdout = subprocess.check_output(args)
@@ -1653,13 +1670,21 @@
     if not map_path.endswith('.map') and not map_path.endswith('.map.gz'):
       parser.error('Expected --map-file to end with .map or .map.gz')
   elif elf_path:
-    map_path = elf_path + '.map'
+    # Look for a .map file named for either the ELF file, or in the partitioned
+    # native library case, the combined ELF file from which the main library was
+    # extracted. Note that we don't yet have a tool_prefix to use here, but
+    # that's not a problem for this use case.
+    if _ElfIsMainPartition(elf_path, ''):
+      map_path = elf_path.replace('.so', '__combined.so') + '.map'
+    else:
+      map_path = elf_path + '.map'
     if not os.path.exists(map_path):
       map_path += '.gz'
     if not os.path.exists(map_path):
       parser.error('Could not find .map(.gz)? file. Ensure you have built with '
                    'is_official_build=true and generate_linker_map=true, or '
                    'use --map-file to point me a linker map file.')
+
   tool_prefix = None
   if map_path:
     linker_name = _DetectLinkerName(map_path)
diff --git a/tools/binary_size/libsupersize/ninja_parser.py b/tools/binary_size/libsupersize/ninja_parser.py
index 97db8eb..82f0ac7 100755
--- a/tools/binary_size/libsupersize/ninja_parser.py
+++ b/tools/binary_size/libsupersize/ninja_parser.py
@@ -16,7 +16,8 @@
 # build obj/.../foo.o: cxx gen/.../foo.cc || obj/.../foo.inputdeps.stamp
 # build obj/.../libfoo.a: alink obj/.../a.o obj/.../b.o |
 # build ./libchrome.so ./lib.unstripped/libchrome.so: solink a.o b.o ...
-_REGEX = re.compile(r'build ([^:]+): \w+ (.*?)(?: \||\n|$)')
+# build libmonochrome.so: __chrome_android_libmonochrome___rule | ...
+_REGEX = re.compile(r'build ([^:]+): \w+ (.*?)(?: *\||\n|$)')
 
 
 class _SourceMapper(object):
@@ -65,7 +66,7 @@
 
 def _ParseNinjaPathList(path_list):
   ret = path_list.replace('\\ ', '\b')
-  return [s.replace('\b', ' ') for s in ret.split(' ')]
+  return [s.replace('\b', ' ') for s in ret.split()]
 
 
 def _ParseOneFile(lines, dep_map, elf_path):
@@ -94,6 +95,10 @@
   return sub_ninjas, elf_inputs
 
 
+def ParseOneFileForTest(lines, dep_map, elf_path):
+  return _ParseOneFile(lines, dep_map, elf_path)
+
+
 def Parse(output_directory, elf_path):
   """Parses build.ninja and subninjas.
 
diff --git a/tools/binary_size/libsupersize/ninja_parser_test.py b/tools/binary_size/libsupersize/ninja_parser_test.py
new file mode 100755
index 0000000..12d72ed
--- /dev/null
+++ b/tools/binary_size/libsupersize/ninja_parser_test.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+import ninja_parser
+
+
+class NinjaParserTest(unittest.TestCase):
+
+  def _ParseOneFile(self, line, lib, expected_inputs, expected_dep_map):
+    """Exercises ninja_parser's ParseOneFile method.
+
+    This allows coverage of most parsing capabilities, without having to
+    synthesize the actual Ninja files expected by the top-level Parse()
+    method.
+    """
+    dep_map = {}
+    _, found_inputs = ninja_parser.ParseOneFileForTest([line], dep_map, lib)
+    self.assertEqual(expected_inputs, found_inputs)
+    self.assertEqual(expected_dep_map, dep_map)
+
+  # These cases cover finding ELF outputs with associated inputs:
+
+  def test_ExplicitDep(self):
+    line = 'build libfoo.so: link a.o'
+    inputs = ['a.o']
+    self._ParseOneFile(line, 'libfoo.so', inputs, {})
+
+  def test_MultipleExplicitDeps(self):
+    line = 'build libfoo.so: link a.o b.o'
+    inputs = ['a.o', 'b.o']
+    self._ParseOneFile(line, 'libfoo.so', inputs, {})
+
+  def test_ExplicitDepWithImplicitDep(self):
+    line = 'build libfoo.so: link a.o | b.o'
+    inputs = ['a.o']
+    self._ParseOneFile(line, 'libfoo.so', inputs, {})
+
+  def test_ExplicitDepWithOrderDep(self):
+    line = 'build libfoo.so: link a.o || b.o'
+    inputs = ['a.o']
+    self._ParseOneFile(line, 'libfoo.so', inputs, {})
+
+  def test_NoExplicitInput(self):
+    line = 'build libfoo.so: custom_link | custom.py'
+    inputs = []
+    self._ParseOneFile(line, 'libfoo.so', inputs, {})
+
+  def test_SpacesInPaths(self):
+    line = 'build libfoo.so: link a\ a.o b\ b.o'
+    inputs = ['a a.o', 'b b.o']
+    self._ParseOneFile(line, 'libfoo.so', inputs, {})
+
+  # These cases cover Object file outputs that update a dependency map:
+
+  def test_ObjectOutputWithExplicitDep(self):
+    line = 'build libfoo.o: cxx a.cc'
+    dep_map = {'libfoo.o': 'a.cc'}
+    self._ParseOneFile(line, 'libfoo.o', None, dep_map)
+
+  def test_ObjectOutputWithExplicitDeps(self):
+    line = 'build libfoo.o: cxx a.cc b.cc'
+    dep_map = {'libfoo.o': 'a.cc b.cc'}
+    self._ParseOneFile(line, 'libfoo.o', None, dep_map)
+
+  def test_ObjectOutputWithOrderDep(self):
+    line = 'build libfoo.o: cxx a.cc || a.inputdeps.stamp'
+    dep_map = {'libfoo.o': 'a.cc'}
+    self._ParseOneFile(line, 'libfoo.o', None, dep_map)
+
+  def test_ObjectOutputWithExplicitDepsAndOrderDep(self):
+    line = 'build libfoo.o: cxx a.cc b.cc || a.inputdeps.stamp'
+    dep_map = {'libfoo.o': 'a.cc b.cc'}
+    self._ParseOneFile(line, 'libfoo.o', None, dep_map)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/binary_size/libsupersize/run_tests.py b/tools/binary_size/libsupersize/run_tests.py
new file mode 100755
index 0000000..1a27082
--- /dev/null
+++ b/tools/binary_size/libsupersize/run_tests.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env vpython
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import sys
+import unittest
+
+if __name__ == '__main__':
+  logging.basicConfig(
+      level=logging.DEBUG if '-v' in sys.argv else logging.WARNING,
+      format='%(levelname)5s %(filename)15s(%(lineno)3d): %(message)s')
+
+  suite = unittest.TestSuite()
+  loader = unittest.TestLoader()
+  suite.addTests(
+      loader.discover(start_dir=os.path.dirname(__file__), pattern='*_test.py'))
+  res = unittest.TextTestRunner(verbosity=2).run(suite)
+  if res.wasSuccessful():
+    sys.exit(0)
+  else:
+    sys.exit(1)
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index 9fc4c20..4637e56d 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -471,10 +471,6 @@
                      '-DCOMPILER_RT_USE_LIBCXX=NO',
                      # Don't run Go bindings tests; PGO makes them confused.
                      '-DLLVM_INCLUDE_GO_TESTS=OFF',
-                     # Don't build libfuzzer. It needs to be built using the
-                     # same C++ standard library as the code that's going to use
-                     # it.
-                     '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
                      ]
 
   if args.gcc_toolchain:
@@ -524,7 +520,6 @@
         '-DCMAKE_INSTALL_PREFIX=' + LLVM_BOOTSTRAP_INSTALL_DIR,
         '-DCMAKE_C_FLAGS=' + ' '.join(cflags),
         '-DCMAKE_CXX_FLAGS=' + ' '.join(cxxflags),
-        '-DCOMPILER_RT_BUILD_XRAY=OFF',
         # Ignore args.disable_asserts for the bootstrap compiler.
         '-DLLVM_ENABLE_ASSERTIONS=ON',
         ]
@@ -544,6 +539,17 @@
           '-DCOMPILER_RT_ENABLE_WATCHOS=OFF',
           '-DCOMPILER_RT_ENABLE_TVOS=OFF',
           ])
+    elif args.pgo:
+      # PGO needs libclang_rt.profile but none of the other compiler-rt stuff.
+      bootstrap_args.extend([
+          '-DCOMPILER_RT_BUILD_BUILTINS=OFF',
+          '-DCOMPILER_RT_BUILD_CRT=OFF',
+          '-DCOMPILER_RT_BUILD_LIBFUZZER=OFF',
+          '-DCOMPILER_RT_BUILD_PROFILE=ON',
+          '-DCOMPILER_RT_BUILD_SANITIZERS=OFF',
+          '-DCOMPILER_RT_BUILD_XRAY=OFF',
+          ])
+
     if cc is not None:  bootstrap_args.append('-DCMAKE_C_COMPILER=' + cc)
     if cxx is not None: bootstrap_args.append('-DCMAKE_CXX_COMPILER=' + cxx)
     if lld is not None: bootstrap_args.append('-DCMAKE_LINKER=' + lld)
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 4fdd2f5..614e957 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -39,7 +39,7 @@
 # Reverting problematic clang rolls is safe, though.
 CLANG_REVISION = 'f7e52fbdb5a7af8ea0808e98458b497125a5eca1'
 CLANG_SVN_REVISION = '365097'
-CLANG_SUB_REVISION = 6
+CLANG_SUB_REVISION = 8
 
 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
                                 CLANG_SUB_REVISION)
diff --git a/tools/json_schema_compiler/feature_compiler.py b/tools/json_schema_compiler/feature_compiler.py
index 5ab30e9dd..d29e285 100644
--- a/tools/json_schema_compiler/feature_compiler.py
+++ b/tools/json_schema_compiler/feature_compiler.py
@@ -150,7 +150,6 @@
           'blessed_extension': 'Feature::BLESSED_EXTENSION_CONTEXT',
           'blessed_web_page': 'Feature::BLESSED_WEB_PAGE_CONTEXT',
           'content_script': 'Feature::CONTENT_SCRIPT_CONTEXT',
-          'extension_service_worker': 'Feature::SERVICE_WORKER_CONTEXT',
           'lock_screen_extension': 'Feature::LOCK_SCREEN_EXTENSION_CONTEXT',
           'web_page': 'Feature::WEB_PAGE_CONTEXT',
           'webui': 'Feature::WEBUI_CONTEXT',
@@ -170,6 +169,9 @@
         'subtype': str
       }
     },
+    'disallow_for_service_workers': {
+      bool: {}
+    },
     'extension_types': {
       list: {
         'enum_map': {
diff --git a/tools/json_schema_compiler/feature_compiler_test.py b/tools/json_schema_compiler/feature_compiler_test.py
index 896afdfc..e7874cb9 100755
--- a/tools/json_schema_compiler/feature_compiler_test.py
+++ b/tools/json_schema_compiler/feature_compiler_test.py
@@ -51,6 +51,7 @@
       ],
       'default_parent': True,
       'dependencies': ['dependency1', 'dependency2'],
+      'disallow_for_service_workers': True,
       'extension_types': ['extension'],
       'location': 'component',
       'internal': True,
diff --git a/tools/json_schema_compiler/test/features_generation_unittest.cc b/tools/json_schema_compiler/test/features_generation_unittest.cc
index 64e363e..678cab1 100644
--- a/tools/json_schema_compiler/test/features_generation_unittest.cc
+++ b/tools/json_schema_compiler/test/features_generation_unittest.cc
@@ -191,7 +191,6 @@
                            Feature::BLESSED_WEB_PAGE_CONTEXT,
                            Feature::CONTENT_SCRIPT_CONTEXT,
                            Feature::LOCK_SCREEN_EXTENSION_CONTEXT,
-                           Feature::SERVICE_WORKER_CONTEXT,
                            Feature::WEB_PAGE_CONTEXT,
                            Feature::WEBUI_CONTEXT,
                            Feature::UNBLESSED_EXTENSION_CONTEXT};
diff --git a/tools/json_schema_compiler/test/features_test.json b/tools/json_schema_compiler/test/features_test.json
index 5cd6155..fa763a9 100644
--- a/tools/json_schema_compiler/test/features_test.json
+++ b/tools/json_schema_compiler/test/features_test.json
@@ -61,7 +61,8 @@
     "channel": "beta",
     "contexts": ["blessed_extension"],
     "extension_types": ["extension"],
-    "whitelist": ["0123456789ABCDEF0123456789ABCDEF01234567"]
+    "whitelist": ["0123456789ABCDEF0123456789ABCDEF01234567"],
+    "disallow_for_service_workers": false
   }, {
     "channel": "stable",
     "contexts": ["blessed_extension"],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index be0f71d3..8a80d64 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -635,6 +635,7 @@
       'android-kitkat-arm-rel': 'android_release_trybot',
       'android-marshmallow-arm64-rel': 'gpu_tests_android_release_trybot_arm64_resource_whitelisting',
       'android-oreo-arm64-cts-networkservice-dbg': 'android_debug_trybot_arm64',
+      'android-oreo-arm64-rel': 'android_release_trybot_arm64_webview_google',
       'android_archive_rel_ng': 'android_release_trybot',
       'android_arm64_dbg_recipe': 'android_debug_trybot_compile_only_arm64',
       'android-binary-size': 'android_binary_size',
@@ -662,11 +663,11 @@
       'gpu-fyi-try-android-m-nexus-9-64': 'gpu_tests_android_release_trybot_arm64',
       'gpu-fyi-try-android-n-nvidia-shield-tv-64': 'gpu_tests_android_release_trybot_arm64',
       'gpu-fyi-try-android-p-pixel-2-32': 'gpu_tests_android_release_trybot',
-      'gpu-fyi-try-android-p-pixel-2-32-vk': 'gpu_tests_android_vulkan_release_trybot',
-      'gpu-fyi-try-android-p-pixel-2-32-deqp-vk': 'deqp_android_vulkan_release_trybot',
-      'gpu-fyi-try-android-p-pixel-2-64-vk': 'gpu_tests_android_vulkan_release_trybot_arm64',
-      'gpu-fyi-try-android-p-pixel-2-64-deqp-vk': 'deqp_android_vulkan_release_trybot_arm64',
       'gpu-fyi-try-android-p-pixel-2-skv-32': 'gpu_tests_android_release_trybot',
+      'gpu-fyi-try-android-q-pixel-2-deqp-vk-32': 'deqp_android_vulkan_release_trybot',
+      'gpu-fyi-try-android-q-pixel-2-deqp-vk-64': 'deqp_android_vulkan_release_trybot_arm64',
+      'gpu-fyi-try-android-q-pixel-2-vk-32': 'gpu_tests_android_vulkan_release_trybot',
+      'gpu-fyi-try-android-q-pixel-2-vk-64': 'gpu_tests_android_vulkan_release_trybot_arm64',
       'linux_android_dbg_ng': 'android_debug_trybot',
       'try-nougat-phone-tester': 'android_debug_trybot_arm64',
       'android-oreo-arm64-dbg': 'android_debug_trybot_arm64',
@@ -1100,6 +1101,11 @@
       'android', 'release_trybot', 'arm64', 'strip_debug_info',
     ],
 
+    'android_release_trybot_arm64_webview_google': [
+      'android', 'release_trybot', 'arm64', 'strip_debug_info',
+      'webview_google',
+    ],
+
     'android_shared_release_bot_x64': [
       'android_without_codecs', 'shared_release_bot', 'x64', 'dcheck_always_on',
     ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 498dec7..bcb8707 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2412,18 +2412,18 @@
   <int value="8" label="ARC app preferred and pressed"/>
   <int value="9" label="Progressive web app pressed"/>
   <int value="10" label="Error before showing picker"/>
+  <int value="11" label="Invalid"/>
+  <int value="12" label="Mac native app pressed"/>
 </enum>
 
 <enum name="ArcIntentHandlerDestinationPlatform">
-  <obsolete>
-    Deprecated as of 9/2018 (M71).
-  </obsolete>
   <summary>
     Defines ARC intent handler platforms to continue the navigation.
   </summary>
   <int value="0" label="ARC"/>
   <int value="1" label="Chrome"/>
   <int value="2" label="Progressive web app"/>
+  <int value="3" label="Mac native app"/>
 </enum>
 
 <enum name="ArcIntentHelperOpenType">
@@ -32890,10 +32890,10 @@
 
 <enum name="LargestContentType">
   <summary>
-    Whether the LargestContentPaint results from image or text. These are the
+    Whether the LargestContentfulPaint results from image or text. These are the
     only content types currently tracked by the metric
-    PageLoad.Experimental.PaintTiming.NavigationToLargestContentPaint, so there
-    is no need for an 'Other' label.
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint, so there is no need
+    for an 'Other' label.
   </summary>
   <int value="0" label="Image"/>
   <int value="1" label="Text"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6a3f6584..605702df 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -6608,7 +6608,7 @@
 </histogram>
 
 <histogram base="true" name="Ash.Overview.AnimationSmoothness.Close" units="%"
-    expires_after="M77">
+    expires_after="M85">
 <!-- Name completed by histogram_suffixes
      name="TabletOrClamshellMode" -->
 
@@ -6621,7 +6621,7 @@
 </histogram>
 
 <histogram base="true" name="Ash.Overview.AnimationSmoothness.Enter" units="%"
-    expires_after="M77">
+    expires_after="M85">
 <!-- Name completed by histogram_suffixes
      name="OverviewAnimationMode" -->
 
@@ -6635,7 +6635,7 @@
 </histogram>
 
 <histogram base="true" name="Ash.Overview.AnimationSmoothness.Exit" units="%"
-    expires_after="M77">
+    expires_after="M85">
 <!-- Name completed by histogram_suffixes
      name="OverviewAnimationMode" -->
 
@@ -89268,7 +89268,11 @@
   <summary>Time spent on error screens during update.</summary>
 </histogram>
 
-<histogram name="OOBE.EULAToSignInTime" units="ms" expires_after="M77">
+<histogram name="OOBE.EULAToSignInTime" units="ms" expires_after="never">
+<!-- expires-never: Core metric for monitoring initial Gaia loading regressions.
+-->
+
+  <owner>rsorokin@chromium.org</owner>
   <owner>alemate@chromium.org</owner>
   <summary>
     Time from acceptance of the EULA until the login screen is first displayed.
@@ -92125,7 +92129,11 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.LargestContentPaint.AllFrames.ContentType"
-    enum="LargestContentType" expires_after="2020-04-23">
+    enum="LargestContentType">
+  <obsolete>
+    Deprecated 7/2019, replaced by
+    PageLoad.PaintTiming.LargestContentfulPaint.ContentType
+  </obsolete>
   <owner>maxlg@chromium.org</owner>
   <owner>npm@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
@@ -92140,7 +92148,11 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.LargestContentPaint.ContentType"
-    enum="LargestContentType" expires_after="2020-04-23">
+    enum="LargestContentType">
+  <obsolete>
+    Deprecated 7/2019, replaced by
+    PageLoad.PaintTiming.LargestContentfulPaint.MainFrame.ContentType
+  </obsolete>
   <owner>maxlg@chromium.org</owner>
   <owner>npm@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
@@ -92169,7 +92181,11 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToLargestContentPaint"
-    units="ms" expires_after="2020-04-23">
+    units="ms">
+  <obsolete>
+    Deprecated 7/2019, replaced by
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint.MainFrame
+  </obsolete>
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -92183,7 +92199,11 @@
 
 <histogram
     name="PageLoad.Experimental.PaintTiming.NavigationToLargestContentPaint.AllFrames"
-    units="ms" expires_after="2020-04-23">
+    units="ms">
+  <obsolete>
+    Deprecated 7/2019, replaced by
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint
+  </obsolete>
   <owner>maxlg@chromium.org</owner>
   <owner>speed-metrics-dev@chromium.org</owner>
   <summary>
@@ -92941,6 +92961,36 @@
 </histogram>
 
 <histogram
+    name="PageLoad.Internal.PaintTiming.LargestContentfulPaint.ContentType"
+    enum="LargestContentType">
+  <owner>maxlg@chromium.org</owner>
+  <owner>npm@chromium.org</owner>
+  <owner>speed-metrics-dev@chromium.org</owner>
+  <summary>
+    Measures whether the largest contentful paint, whose timestamp is measured
+    by PageLoad.PaintTiming.NavigationToLargestContentfulPaint, comes from text
+    or image. This value is recorded whenever
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint is recorded.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Internal.PaintTiming.LargestContentfulPaint.MainFrame.ContentType"
+    enum="LargestContentType">
+  <owner>maxlg@chromium.org</owner>
+  <owner>npm@chromium.org</owner>
+  <owner>speed-metrics-dev@chromium.org</owner>
+  <summary>
+    Measures whether the largest content paint in the main frame, whose
+    timestamp is measured by
+    PageLoad.PaintTiming.NavigationToLargestContentPaint.MainFrame, comes from
+    text or image. This value is recorded whenever
+    PageLoad.PaintTiming.NavigationToLargestContentfulPaint.MainFrame is
+    recorded.
+  </summary>
+</histogram>
+
+<histogram
     name="PageLoad.Internal.PaintTiming.NavigationToFirstContentfulPaint.InitiatingProcess"
     enum="ProcessType2" expires_after="2019-06-30">
   <owner>sullivan@chromium.org</owner>
@@ -93229,6 +93279,33 @@
   </summary>
 </histogram>
 
+<histogram name="PageLoad.PaintTiming.NavigationToLargestContentfulPaint"
+    units="ms">
+  <owner>maxlg@chromium.org</owner>
+  <owner>speed-metrics-dev@chromium.org</owner>
+  <summary>
+    Measures the time from navigation timing's navigation start to the time the
+    largest content (text or image) is first painted, across all frames.
+    Excludes any content painted after user input. The value is recorded at the
+    end of each page load unless there is an abort or user input before text or
+    image paint. See http://bit.ly/fcp_plus_plus for details.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.PaintTiming.NavigationToLargestContentfulPaint.MainFrame"
+    units="ms">
+  <owner>maxlg@chromium.org</owner>
+  <owner>speed-metrics-dev@chromium.org</owner>
+  <summary>
+    Measures the time from navigation timing's navigation start to the time the
+    largest content (text or image) is first painted, for main frame documents.
+    Excludes any content painted after user input. The value is recorded at the
+    end of each page load unless there is an abort or user input before text or
+    image paint. See http://bit.ly/fcp_plus_plus for details.
+  </summary>
+</histogram>
+
 <histogram name="PageLoad.PaintTiming.ParseStartToFirstContentfulPaint"
     units="ms" expires_after="never">
 <!-- expires-never: "heartbeat" metric (internal: go/uma-heartbeats) -->
@@ -115425,6 +115502,10 @@
 
 <histogram name="SBClientDownload.ExtractImageHeadersTime" units="ms"
     expires_after="M77">
+  <obsolete>
+    Deprecated in M77. Extracting image headers was usually fast (84% were less
+    than 1ms), so this histogram was not providing useful data.
+  </obsolete>
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <owner>grt@chromium.org</owner>
@@ -142890,10 +142971,12 @@
 </histogram>
 
 <histogram name="UserManager.LogoutToLoginDelay" units="seconds"
-    expires_after="M77">
+    expires_after="never">
+<!-- expires-never: Core metric for monitoring Chrome OS logout/login
+regressions. -->
+
   <owner>alemate@chromium.org</owner>
-  <owner>omrilio@chromium.org</owner>
-  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>rsorokin@chromium.org</owner>
   <summary>
     The time between one regular user logging out and a different regular user
     logging in (Chrome OS). Delays above thirty minutes or which span system
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 114de02..64b5a69 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -4817,6 +4817,10 @@
     </aggregation>
   </metric>
   <metric name="Experimental.PaintTiming.NavigationToLargestContentPaint">
+    <obsolete>
+      Deprecated 7/2019, replaced by
+      PaintTiming.NavigationToLargestContentfulPaint.MainFrame.
+    </obsolete>
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
       start to the time when the page first paints the largest content (text or
@@ -4825,6 +4829,10 @@
   </metric>
   <metric
       name="Experimental.PaintTiming.NavigationToLargestContentPaintAllFrames">
+    <obsolete>
+      Deprecated 7/2019, replaced by
+      PaintTiming.NavigationToLargestContentfulPaint
+    </obsolete>
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
       start to the time when the page first paints the largest content (text or
@@ -5343,6 +5351,22 @@
       </history>
     </aggregation>
   </metric>
+  <metric name="PaintTiming.NavigationToLargestContentfulPaint">
+    <summary>
+      Measures the time in milliseconds from navigation timing's navigation
+      start to the time when the page first paints the largest content (text or
+      image) within viewport, across all frames. See http://bit.ly/fcp_plus_plus
+      for more details.
+    </summary>
+  </metric>
+  <metric name="PaintTiming.NavigationToLargestContentfulPaint.MainFrame">
+    <summary>
+      Measures the time in milliseconds from navigation timing's navigation
+      start to the time when the page first paints the largest content (text or
+      image) within viewport, in the main frame. See http://bit.ly/fcp_plus_plus
+      for more details.
+    </summary>
+  </metric>
   <metric name="ParseTiming.NavigationToParseStart">
     <summary>
       Measures the time in milliseconds from navigation timing's navigation
@@ -6208,6 +6232,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -7795,6 +7822,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -7823,6 +7853,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -7845,6 +7878,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -7901,6 +7937,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -7964,6 +8003,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -7986,6 +8028,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
@@ -8014,6 +8059,9 @@
         <index fields="profile.country"/>
         <index fields="profile.form_factor"/>
         <index fields="profile.system_ram"/>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
       </history>
     </aggregation>
   </metric>
diff --git a/tools/perf/core/minidump_unittest.py b/tools/perf/core/minidump_unittest.py
index 4085a37..6b236224 100644
--- a/tools/perf/core/minidump_unittest.py
+++ b/tools/perf/core/minidump_unittest.py
@@ -15,9 +15,8 @@
 class BrowserMinidumpTest(tab_test_case.TabTestCase):
   @decorators.Isolated
   # ChromeOS and Android are currently hard coded to return None for minidump
-  # paths, so disable on those platforms. Windows 7 doesn't find any minidump
-  # paths for some reason.
-  @decorators.Disabled('chromeos', 'android', 'win7')
+  # paths, so disable on those platforms.
+  @decorators.Disabled('chromeos', 'android')
   def testSymbolizeMinidump(self):
     # Wait for the browser to restart fully before crashing
     self._LoadPageThenWait('var sam = "car";', 'sam')
@@ -89,10 +88,7 @@
       self.assertTrue(crash_function in sections[4])
 
   @decorators.Isolated
-  # Disabled on Mac 10.12 (Sierra) due to it not getting a stack trace to
-  # symbolize from the second crash.
-  # Test is flaky on 10.13 (HighSierra). See https://crbug.com/986644.
-  @decorators.Disabled('chromeos', 'android', 'win7', 'sierra', 'highsierra')
+  @decorators.Disabled('chromeos', 'android')
   def testMultipleCrashMinidumps(self):
     # Wait for the browser to restart fully before crashing
     self._LoadPageThenWait('var cat = "dog";', 'cat')
diff --git a/tools/polymer/polymer.py b/tools/polymer/polymer.py
index adab3591..fcb0302 100644
--- a/tools/polymer/polymer.py
+++ b/tools/polymer/polymer.py
@@ -114,7 +114,8 @@
       return self.html_path_normalized
 
     input_dir = os.path.relpath(os.path.dirname(self.html_file), _ROOT)
-    return os.path.normpath(os.path.join(input_dir, self.html_path))
+    return os.path.normpath(
+        os.path.join(input_dir, self.html_path)).replace("\\", "/")
 
   def _to_js_normalized(self):
     if re.match(POLYMER_V1_DIR, self.html_path_normalized):
@@ -145,7 +146,8 @@
       return js_path
 
     input_dir = os.path.relpath(os.path.dirname(self.html_file), _ROOT)
-    relpath = os.path.relpath(self.js_path_normalized, input_dir)
+    relpath = os.path.relpath(
+        self.js_path_normalized, input_dir).replace("\\", "/")
     # Prepend "./" if |relpath| refers to a relative subpath, that is not "../".
     # This prefix is required for JS Modules paths.
     if not relpath.startswith('.'):
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index d5458e6..161f0b5 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -33,6 +33,9 @@
 
 namespace views {
 
+// static
+bool BubbleDialogDelegateView::devtools_dismiss_override_ = false;
+
 namespace {
 
 // Override base functionality of Widget to give bubble dialogs access to the
@@ -222,6 +225,9 @@
 
 void BubbleDialogDelegateView::OnWidgetActivationChanged(Widget* widget,
                                                          bool active) {
+  if (devtools_dismiss_override_)
+    return;
+
 #if defined(OS_MACOSX)
   // Install |mac_bubble_closer_| the first time the widget becomes active.
   if (widget == GetWidget() && active && !mac_bubble_closer_) {
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index e11681e..77288a2 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -30,6 +30,10 @@
 class Accelerator;
 }  // namespace ui
 
+namespace ui_devtools {
+class PageAgentViews;
+}
+
 namespace views {
 
 class BubbleFrameView;
@@ -192,6 +196,7 @@
  private:
   friend class BubbleBorderDelegate;
   friend class BubbleWindowTargeter;
+  friend class ui_devtools::PageAgentViews;
 
   FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CreateDelegate);
   FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, NonClientHitTest);
@@ -210,6 +215,10 @@
   // provide different highlight effects.
   virtual void UpdateHighlightedButton(bool highlighted);
 
+  // Set from UI DevTools to prevent bubbles from closing in
+  // OnWidgetActivationChanged().
+  static bool devtools_dismiss_override_;
+
   // A flag controlling bubble closure on deactivation.
   bool close_on_deactivate_;
 
diff --git a/ui/webui/resources/js/assert.js b/ui/webui/resources/js/assert.js
index 1c7c227..a7c1bfa 100644
--- a/ui/webui/resources/js/assert.js
+++ b/ui/webui/resources/js/assert.js
@@ -23,9 +23,10 @@
     }
     const error = new Error(message);
     const global = function() {
+      const thisOrSelf = this || self;
       /** @type {boolean} */
-      this.traceAssertionsForTesting;
-      return this;
+      thisOrSelf.traceAssertionsForTesting;
+      return thisOrSelf;
     }();
     if (global.traceAssertionsForTesting) {
       console.warn(error.stack);