diff --git a/.gitignore b/.gitignore
index b444da6..c47ffece 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,7 +76,6 @@
 /chrome/build/chrome.x86.orderfile
 /chrome/build/chrome_child.x64.orderfile
 /chrome/build/chrome_child.x86.orderfile
-/chrome/content_gl_tests_run.xml
 /chrome/gl_tests_run.xml
 /chrome/gles2_conform_test_run.xml
 /chrome/tab_capture_performance_tests_run.xml
diff --git a/AUTHORS b/AUTHORS
index 0a70fcb..d20332e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -696,6 +696,7 @@
 Po-Chun Chang <pochang0403@gmail.com>
 Pramod Begur Srinath <pramod.bs@samsung.com>
 Pranay Kumar <pranay.kumar@samsung.com>
+Pranjal Jumde <pranjal@brave.com>
 Prashant Hiremath <prashhir@cisco.com>
 Prashant Nevase <prashant.n@samsung.com>
 Prashant Patil <prashant.patil@imgtec.com>
diff --git a/DEPS b/DEPS
index 2f008c12..e86cf84 100644
--- a/DEPS
+++ b/DEPS
@@ -116,11 +116,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': '12597f9b831d49baa707d5419e259cb53e793197',
+  'skia_revision': 'd4fdf78475a195ad1b83a5f51ad8d923f0150a33',
   # 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': '50fddcc942f5a57668b67a6361c003daf0a2f74e',
+  'v8_revision': '79c9d04766de9a845be146902f6503bdd3ff0ae6',
   # 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.
@@ -140,7 +140,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': '8da0e1b3c27a0bcaac3b3fe08f752506d8042288',
+  'pdfium_revision': 'd19dc58f1102216d472edfe2843c85bf2f4a1a7c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -1194,7 +1194,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'f22b9ad8d75bf99652dfc3b1db40e73d9fdd3840',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '83ed89a45f4578ca07efef48e772b9aafb263163',
+    Var('webrtc_git') + '/src.git' + '@' + '74ba99062c48b278675cfe52643719202296fddc',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1235,7 +1235,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a24149c46be6c8ae43f329b2289867998bdcc46e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@902711febd7b3ad6a55ee9a0e4dcd16f3aa2bc22',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index e2bb530..26df15f 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -394,19 +394,21 @@
 
 void AppListItemView::SetItemName(const base::string16& display_name,
                                   const base::string16& full_name) {
-  if (is_folder_ && display_name.empty()) {
-    title_->SetText(ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
-        IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER));
-  } else {
+  const base::string16 folder_name_placeholder =
+      ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
+          IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER);
+  if (is_folder_ && display_name.empty())
+    title_->SetText(folder_name_placeholder);
+  else
     title_->SetText(display_name);
-  }
 
   tooltip_text_ = display_name == full_name ? base::string16() : full_name;
 
   // Use full name for accessibility.
   SetAccessibleName(
       is_folder_ ? l10n_util::GetStringFUTF16(
-                       IDS_APP_LIST_FOLDER_BUTTON_ACCESSIBILE_NAME, full_name)
+                       IDS_APP_LIST_FOLDER_BUTTON_ACCESSIBILE_NAME,
+                       full_name.empty() ? folder_name_placeholder : full_name)
                  : full_name);
   Layout();
 }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 21c8dd5..768c03d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2945,6 +2945,7 @@
       "android/java/src/org/chromium/base/EventLog.java",
       "android/java/src/org/chromium/base/FieldTrialList.java",
       "android/java/src/org/chromium/base/FileUtils.java",
+      "android/java/src/org/chromium/base/GcStateAssert.java",
       "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
       "android/java/src/org/chromium/base/IntStringCallback.java",
       "android/java/src/org/chromium/base/JNIUtils.java",
@@ -3035,6 +3036,10 @@
       "*/NativeLibraries.class",
       "*/NativeLibraries##*.class",
     ]
+
+    if (!is_java_debug && !dcheck_always_on) {
+      proguard_configs = [ "android/proguard/dcheck_is_off.flags" ]
+    }
   }
 
   android_aidl("base_java_aidl") {
@@ -3192,6 +3197,7 @@
       "android/junit/src/org/chromium/base/AnimationFrameTimeHistogramTest.java",
       "android/junit/src/org/chromium/base/ApplicationStatusTest.java",
       "android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java",
+      "android/junit/src/org/chromium/base/GcStateAssertTest.java",
       "android/junit/src/org/chromium/base/LogTest.java",
       "android/junit/src/org/chromium/base/NonThreadSafeTest.java",
       "android/junit/src/org/chromium/base/PiiEliderTest.java",
diff --git a/base/android/java/src/org/chromium/base/GcStateAssert.java b/base/android/java/src/org/chromium/base/GcStateAssert.java
new file mode 100644
index 0000000..b4128c9
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/GcStateAssert.java
@@ -0,0 +1,119 @@
+// 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.base;
+
+import android.support.annotation.VisibleForTesting;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Used to assert that clean-up logic has been run before an object is GC'ed.
+ *
+ * Class is a no-op withen DCHECK_IS_ON=false, and is entirely removed by
+ * proguard (enforced via -checkdiscard).
+ *
+ * Usage:
+ * class MyClassWithCleanup {
+ *     private final mGcStateAssert = GcStateAssert.create(this);
+ *
+ *     public void destroy() {
+ *         // If mGcStateAssert is GC'ed before this is called, it will throw an exception
+ *         // with a stack trace showing the stack during GcStateAssert.create().
+ *         GcStateAssert.setSafeToGc(mGcStateAssert, true);
+ *     }
+ * }
+ */
+public class GcStateAssert {
+    interface TestHook {
+        void onCleaned(WrappedReference ref, String msg);
+    }
+
+    // Used only for unit test.
+    static TestHook sTestHook;
+
+    @VisibleForTesting
+    final WrappedReference mWrapper;
+
+    @VisibleForTesting
+    static class WrappedReference extends PhantomReference<Object> {
+        boolean mSafeToGc;
+        final Class<?> mTargetClass;
+        final Throwable mCreationException;
+
+        public WrappedReference(Object target, boolean safeToGc) {
+            super(target, sReferenceQueue);
+            mSafeToGc = safeToGc;
+            mTargetClass = target.getClass();
+            // Create an exception to capture stack trace of when object was created.
+            mCreationException = new RuntimeException();
+            sActiveWrappers.add(this);
+        }
+
+        private static ReferenceQueue<Object> sReferenceQueue = new ReferenceQueue<>();
+        private static Set<WrappedReference> sActiveWrappers =
+                Collections.synchronizedSet(new HashSet<>());
+
+        static {
+            new Thread("GcStateAssertQueue") {
+                {
+                    setDaemon(true);
+                    start();
+                }
+
+                @Override
+                public void run() {
+                    while (true) {
+                        try {
+                            // This sleeps until a wrapper is available.
+                            WrappedReference wrapper = (WrappedReference) sReferenceQueue.remove();
+                            sActiveWrappers.remove(wrapper);
+                            if (!wrapper.mSafeToGc) {
+                                String msg = String.format(
+                                        "Object of type %s was GC'ed without cleanup. Refer to "
+                                                + "\"Caused by\" for where object was created.",
+                                        wrapper.mTargetClass.getName());
+                                if (sTestHook != null) {
+                                    sTestHook.onCleaned(wrapper, msg);
+                                } else {
+                                    throw new RuntimeException(msg, wrapper.mCreationException);
+                                }
+                            } else if (sTestHook != null) {
+                                sTestHook.onCleaned(wrapper, null);
+                            }
+                        } catch (InterruptedException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                }
+            };
+        }
+    }
+
+    private GcStateAssert(WrappedReference wrapper) {
+        mWrapper = wrapper;
+    }
+
+    public static GcStateAssert create(Object target) {
+        return create(target, false);
+    }
+
+    public static GcStateAssert create(Object target, boolean safeToGc) {
+        if (!BuildConfig.DCHECK_IS_ON) {
+            return null;
+        }
+        return new GcStateAssert(new WrappedReference(target, safeToGc));
+    }
+
+    public static void setSafeToGc(GcStateAssert asserter, boolean value) {
+        if (BuildConfig.DCHECK_IS_ON) {
+            // asserter is never null when DCHECK_IS_ON.
+            asserter.mWrapper.mSafeToGc = value;
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
index fd8d048..ece29b4 100644
--- a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
+++ b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
@@ -29,7 +29,7 @@
     }
 
     @Override
-    public void postTask(TaskTraits taskTraits, Runnable task) {
-        createTaskRunner(taskTraits).postTask(task);
+    public void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay) {
+        createTaskRunner(taskTraits).postDelayedTask(task, delay);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/task/PostTask.java b/base/android/java/src/org/chromium/base/task/PostTask.java
index 8834439..922051e 100644
--- a/base/android/java/src/org/chromium/base/task/PostTask.java
+++ b/base/android/java/src/org/chromium/base/task/PostTask.java
@@ -36,14 +36,7 @@
      */
     public static TaskRunner createTaskRunner(TaskTraits taskTraits) {
         synchronized (sLock) {
-            TaskRunner taskRunner =
-                    getTaskExecutorForTraits(taskTraits).createTaskRunner(taskTraits);
-            if (sPreNativeTaskRunners != null) {
-                sPreNativeTaskRunners.add(taskRunner);
-            } else {
-                taskRunner.initNativeTaskRunner();
-            }
-            return taskRunner;
+            return getTaskExecutorForTraits(taskTraits).createTaskRunner(taskTraits);
         }
     }
 
@@ -53,14 +46,7 @@
      */
     public static SequencedTaskRunner createSequencedTaskRunner(TaskTraits taskTraits) {
         synchronized (sLock) {
-            SequencedTaskRunner taskRunner =
-                    getTaskExecutorForTraits(taskTraits).createSequencedTaskRunner(taskTraits);
-            if (sPreNativeTaskRunners != null) {
-                sPreNativeTaskRunners.add(taskRunner);
-            } else {
-                taskRunner.initNativeTaskRunner();
-            }
-            return taskRunner;
+            return getTaskExecutorForTraits(taskTraits).createSequencedTaskRunner(taskTraits);
         }
     }
 
@@ -71,14 +57,7 @@
      */
     public static SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits taskTraits) {
         synchronized (sLock) {
-            SingleThreadTaskRunner taskRunner =
-                    getTaskExecutorForTraits(taskTraits).createSingleThreadTaskRunner(taskTraits);
-            if (sPreNativeTaskRunners != null) {
-                sPreNativeTaskRunners.add(taskRunner);
-            } else {
-                taskRunner.initNativeTaskRunner();
-            }
-            return taskRunner;
+            return getTaskExecutorForTraits(taskTraits).createSingleThreadTaskRunner(taskTraits);
         }
     }
 
@@ -87,13 +66,22 @@
      * @param task The task to be run with the specified traits.
      */
     public static void postTask(TaskTraits taskTraits, Runnable task) {
+        postDelayedTask(taskTraits, task, 0);
+    }
+
+    /**
+     * @param taskTraits The TaskTraits that describe the desired TaskRunner.
+     * @param task The task to be run with the specified traits.
+     * @param delay The delay in milliseconds before the task can be run.
+     */
+    public static void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay) {
         synchronized (sLock) {
             if (sPreNativeTaskRunners != null) {
-                getTaskExecutorForTraits(taskTraits).postTask(taskTraits, task);
+                getTaskExecutorForTraits(taskTraits).postDelayedTask(taskTraits, task, delay);
             } else {
-                nativePostTask(taskTraits.mPrioritySetExplicitly, taskTraits.mPriority,
+                nativePostDelayedTask(taskTraits.mPrioritySetExplicitly, taskTraits.mPriority,
                         taskTraits.mMayBlock, taskTraits.mExtensionId, taskTraits.mExtensionData,
-                        task);
+                        task, delay);
             }
         }
     }
@@ -113,6 +101,23 @@
         }
     }
 
+    /**
+     * Called by every TaskRunner on its creation, attempts to register this
+     * TaskRunner as pre-native, unless the native scheduler has been
+     * initialised already, and informs the caller about the outcome. Called
+     * only when sLock has already been acquired.
+     *
+     * @param taskRunner The TaskRunner to be registered.
+     * @return If the taskRunner got registered as pre-native.
+     */
+    static boolean registerPreNativeTaskRunnerLocked(TaskRunner taskRunner) {
+        if (sPreNativeTaskRunners != null) {
+            sPreNativeTaskRunners.add(taskRunner);
+            return true;
+        }
+        return false;
+    }
+
     private static TaskExecutor getTaskExecutorForTraits(TaskTraits traits) {
         return sTaskExecutors[traits.mExtensionId];
     }
@@ -136,6 +141,6 @@
         }
     }
 
-    private static native void nativePostTask(boolean prioritySetExplicitly, int priority,
-            boolean mayBlock, byte extensionId, byte[] extensionData, Runnable task);
+    private static native void nativePostDelayedTask(boolean prioritySetExplicitly, int priority,
+            boolean mayBlock, byte extensionId, byte[] extensionData, Runnable task, long delay);
 }
diff --git a/base/android/java/src/org/chromium/base/task/TaskExecutor.java b/base/android/java/src/org/chromium/base/task/TaskExecutor.java
index 101be682..3a9e8cf 100644
--- a/base/android/java/src/org/chromium/base/task/TaskExecutor.java
+++ b/base/android/java/src/org/chromium/base/task/TaskExecutor.java
@@ -13,8 +13,9 @@
     /**
      * @param traits The TaskTraits that describe the desired TaskRunner.
      * @param task The task to be run with the specified traits.
+     * @param delay The delay in milliseconds before the task can be run.
      */
-    public void postTask(TaskTraits traits, Runnable task);
+    public void postDelayedTask(TaskTraits traits, Runnable task, long delay);
 
     /**
      * @param traits The TaskTraits that describe the desired TaskRunner.
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunner.java b/base/android/java/src/org/chromium/base/task/TaskRunner.java
index a50f4fd..b901b64f 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunner.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunner.java
@@ -27,6 +27,14 @@
     void destroy();
 
     /**
+     * Posts a task to run after a specified delay.
+     *
+     * @param task The task to be run.
+     * @param delay The delay in milliseconds before the task can be run.
+     */
+    public void postDelayedTask(Runnable task, long delay);
+
+    /**
      * Instructs the TaskRunner to initialize the native TaskRunner and migrate any tasks over to
      * it.
      */
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
index d059767..3612b5c 100644
--- a/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
+++ b/base/android/java/src/org/chromium/base/task/TaskRunnerImpl.java
@@ -5,11 +5,14 @@
 package org.chromium.base.task;
 
 import android.support.annotation.Nullable;
+import android.util.Pair;
 
 import org.chromium.base.TraceEvent;
 import org.chromium.base.annotations.JNINamespace;
 
+import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.List;
 
 /**
  * Implementation of the abstract class {@link TaskRunnerImpl}. Uses AsyncTasks until
@@ -28,6 +31,8 @@
 
     @Nullable
     protected LinkedList<Runnable> mPreNativeTasks = new LinkedList<>();
+    @Nullable
+    protected List<Pair<Runnable, Long>> mPreNativeDelayedTasks = new ArrayList<>();
 
     /**
      * @param traits The TaskTraits associated with this TaskRunnerImpl.
@@ -47,6 +52,7 @@
         mTaskTraits = traits;
         mTraceEvent = traceCategory + ".PreNativeTask.run";
         mTaskRunnerType = taskRunnerType;
+        if (!PostTask.registerPreNativeTaskRunnerLocked(this)) initNativeTaskRunner();
     }
 
     @Override
@@ -70,15 +76,28 @@
 
     @Override
     public void postTask(Runnable task) {
+        postDelayedTask(task, 0);
+    }
+
+    @Override
+    public void postDelayedTask(Runnable task, long delay) {
         synchronized (mLock) {
             assert !mIsDestroying;
             if (mNativeTaskRunnerAndroid != 0) {
-                nativePostTask(mNativeTaskRunnerAndroid, task);
+                nativePostDelayedTask(mNativeTaskRunnerAndroid, task, delay);
                 return;
             }
             // We don't expect a whole lot of these, if that changes consider pooling them.
-            mPreNativeTasks.add(task);
-            schedulePreNativeTask();
+            // If a task is scheduled for immediate execution, we post it on the
+            // pre-native task runner. Tasks scheduled to run with a delay will
+            // wait until the native task runner is initialised.
+            if (delay == 0) {
+                mPreNativeTasks.add(task);
+                schedulePreNativeTask();
+            } else {
+                Pair<Runnable, Long> preNativeDelayedTask = new Pair<>(task, delay);
+                mPreNativeDelayedTasks.add(preNativeDelayedTask);
+            }
         }
     }
 
@@ -117,9 +136,13 @@
                                 mTaskTraits.mPriority, mTaskTraits.mMayBlock,
                                 mTaskTraits.mExtensionId, mTaskTraits.mExtensionData);
                 for (Runnable task : mPreNativeTasks) {
-                    nativePostTask(mNativeTaskRunnerAndroid, task);
+                    nativePostDelayedTask(mNativeTaskRunnerAndroid, task, 0);
+                }
+                for (Pair<Runnable, Long> task : mPreNativeDelayedTasks) {
+                    nativePostDelayedTask(mNativeTaskRunnerAndroid, task.first, task.second);
                 }
                 mPreNativeTasks = null;
+                mPreNativeDelayedTasks = null;
             }
         }
     }
@@ -129,6 +152,7 @@
             boolean prioritySetExplicitly, int priority, boolean mayBlock, byte extensionId,
             byte[] extensionData);
     private native void nativeDestroy(long nativeTaskRunnerAndroid);
-    private native void nativePostTask(long nativeTaskRunnerAndroid, Runnable task);
+    private native void nativePostDelayedTask(
+            long nativeTaskRunnerAndroid, Runnable task, long delay);
     protected native boolean nativeBelongsToCurrentThread(long nativeTaskRunnerAndroid);
 }
diff --git a/base/android/junit/src/org/chromium/base/GcStateAssertTest.java b/base/android/junit/src/org/chromium/base/GcStateAssertTest.java
new file mode 100644
index 0000000..6460441
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/GcStateAssertTest.java
@@ -0,0 +1,92 @@
+// 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.base;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/**
+ * junit tests for {@link GcStateAssert}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class GcStateAssertTest {
+    private static class TestClass {
+        // Put assert inside of a test class to mirror typical api usage.
+        final GcStateAssert mGcStateAssert = GcStateAssert.create(this);
+    }
+
+    private final Object mLock = new Object();
+    private TestClass mTestClass;
+    private GcStateAssert.WrappedReference mTargetRef;
+    private boolean mFound;
+    private String mHookMessage;
+
+    @Before
+    public void setUp() {
+        mTestClass = new TestClass();
+        mTargetRef = mTestClass.mGcStateAssert.mWrapper;
+        mFound = false;
+        mHookMessage = null;
+        GcStateAssert.sTestHook = (ref, msg) -> {
+            if (ref == mTargetRef) {
+                synchronized (mLock) {
+                    mFound = true;
+                    mHookMessage = msg;
+                    mLock.notify();
+                }
+            }
+        };
+    }
+
+    @After
+    public void tearDown() {
+        GcStateAssert.sTestHook = null;
+    }
+
+    private void runTest(boolean setSafe) {
+        if (!BuildConfig.DCHECK_IS_ON) {
+            return;
+        }
+
+        synchronized (mLock) {
+            if (setSafe) {
+                GcStateAssert.setSafeToGc(mTestClass.mGcStateAssert, true);
+            }
+            // Null out field to make reference GC'able.
+            mTestClass = null;
+            // Call System.gc() until the background thread notices the reference.
+            for (int i = 0; i < 10 && !mFound; ++i) {
+                System.gc();
+                try {
+                    mLock.wait(200);
+                } catch (InterruptedException e) {
+                }
+            }
+            Assert.assertTrue(mFound);
+            if (setSafe) {
+                Assert.assertNull(mHookMessage);
+            } else {
+                Assert.assertNotNull(mHookMessage);
+            }
+        }
+    }
+
+    @Test
+    public void testSafeGc() {
+        runTest(true);
+    }
+
+    @Test
+    public void testUnsafeGc() {
+        runTest(false);
+    }
+}
diff --git a/base/android/proguard/dcheck_is_off.flags b/base/android/proguard/dcheck_is_off.flags
new file mode 100644
index 0000000..b256f83
--- /dev/null
+++ b/base/android/proguard/dcheck_is_off.flags
@@ -0,0 +1,8 @@
+# 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.
+
+# Contains flags that are applied only when ENABLE_DCHECK=false.
+
+# Ensure that GcStateAssert is fully optimized away.
+-checkdiscard class org.chromium.base.GcStateAssert$WrappedReference { *; }
diff --git a/base/android/task_scheduler/post_task_android.cc b/base/android/task_scheduler/post_task_android.cc
index e33d52fc..98319c5c 100644
--- a/base/android/task_scheduler/post_task_android.cc
+++ b/base/android/task_scheduler/post_task_android.cc
@@ -61,23 +61,25 @@
                         extension_id, GetExtensionData(env, extension_data)));
 }
 
-void JNI_PostTask_PostTask(
+void JNI_PostTask_PostDelayedTask(
     JNIEnv* env,
     jboolean priority_set_explicitly,
     jint priority,
     jboolean may_block,
     jbyte extension_id,
     const base::android::JavaParamRef<jbyteArray>& extension_data,
-    const base::android::JavaParamRef<jobject>& task) {
+    const base::android::JavaParamRef<jobject>& task,
+    jlong delay) {
   // This could be run on any java thread, so we can't cache |env| in the
   // BindOnce because JNIEnv is thread specific.
-  PostTaskWithTraits(
+  PostDelayedTaskWithTraits(
       FROM_HERE,
       PostTaskAndroid::CreateTaskTraits(env, priority_set_explicitly, priority,
                                         may_block, extension_id,
                                         extension_data),
       BindOnce(&PostTaskAndroid::RunJavaTask,
-               base::android::ScopedJavaGlobalRef<jobject>(task)));
+               base::android::ScopedJavaGlobalRef<jobject>(task)),
+      TimeDelta::FromMilliseconds(delay));
 }
 
 // static
diff --git a/base/android/task_scheduler/task_runner_android.cc b/base/android/task_scheduler/task_runner_android.cc
index 0f5b1d5..cf61442 100644
--- a/base/android/task_scheduler/task_runner_android.cc
+++ b/base/android/task_scheduler/task_runner_android.cc
@@ -7,6 +7,7 @@
 #include "base/android/task_scheduler/post_task_android.h"
 #include "base/run_loop.h"
 #include "base/task/post_task.h"
+#include "base/time/time.h"
 #include "jni/TaskRunnerImpl_jni.h"
 
 namespace base {
@@ -49,13 +50,16 @@
   delete this;
 }
 
-void TaskRunnerAndroid::PostTask(JNIEnv* env,
-                                 const base::android::JavaRef<jobject>& caller,
-                                 const base::android::JavaRef<jobject>& task) {
-  task_runner_->PostTask(
+void TaskRunnerAndroid::PostDelayedTask(
+    JNIEnv* env,
+    const base::android::JavaRef<jobject>& caller,
+    const base::android::JavaRef<jobject>& task,
+    jlong delay) {
+  task_runner_->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&PostTaskAndroid::RunJavaTask,
-                     base::android::ScopedJavaGlobalRef<jobject>(task)));
+                     base::android::ScopedJavaGlobalRef<jobject>(task)),
+      TimeDelta::FromMilliseconds(delay));
 }
 
 bool TaskRunnerAndroid::BelongsToCurrentThread(
diff --git a/base/android/task_scheduler/task_runner_android.h b/base/android/task_scheduler/task_runner_android.h
index ebec5b8..00ead774 100644
--- a/base/android/task_scheduler/task_runner_android.h
+++ b/base/android/task_scheduler/task_runner_android.h
@@ -22,9 +22,10 @@
 
   void Destroy(JNIEnv* env, const base::android::JavaRef<jobject>& caller);
 
-  void PostTask(JNIEnv* env,
-                const base::android::JavaRef<jobject>& caller,
-                const base::android::JavaRef<jobject>& task);
+  void PostDelayedTask(JNIEnv* env,
+                       const base::android::JavaRef<jobject>& caller,
+                       const base::android::JavaRef<jobject>& task,
+                       jlong delay);
 
   bool BelongsToCurrentThread(JNIEnv* env,
                               const base::android::JavaRef<jobject>& caller);
diff --git a/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java b/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java
index 121ea39..89ac39c9 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/task/SchedulerTestHelpers.java
@@ -24,18 +24,27 @@
 public class SchedulerTestHelpers {
     public static void postRecordOrderTask(
             TaskRunner taskQueue, List<Integer> orderList, int order) {
-        taskQueue.postTask(new Runnable() {
+        postRecordOrderDelayedTask(taskQueue, orderList, order, 0);
+    }
+
+    public static void postRecordOrderDelayedTask(
+            TaskRunner taskQueue, List<Integer> orderList, int order, long delay) {
+        taskQueue.postDelayedTask(new Runnable() {
             @Override
             public void run() {
                 orderList.add(order);
             }
-        });
+        }, delay);
     }
 
     public static void postTaskAndBlockUntilRun(TaskRunner taskQueue) {
+        postDelayedTaskAndBlockUntilRun(taskQueue, 0);
+    }
+
+    public static void postDelayedTaskAndBlockUntilRun(TaskRunner taskQueue, long delay) {
         final Object lock = new Object();
         final AtomicBoolean taskExecuted = new AtomicBoolean();
-        taskQueue.postTask(new Runnable() {
+        taskQueue.postDelayedTask(new Runnable() {
             @Override
             public void run() {
                 synchronized (lock) {
@@ -43,7 +52,7 @@
                     lock.notify();
                 }
             }
-        });
+        }, delay);
         synchronized (lock) {
             try {
                 while (!taskExecuted.get()) {
@@ -55,6 +64,18 @@
         }
     }
 
+    public static void postThreeTasksInOrder(TaskRunner taskQueue, List<Integer> orderList) {
+        postRecordOrderTask(taskQueue, orderList, 1);
+        postRecordOrderTask(taskQueue, orderList, 2);
+        postRecordOrderTask(taskQueue, orderList, 3);
+    }
+
+    public static void postThreeDelayedTasksInOrder(TaskRunner taskQueue, List<Integer> orderList) {
+        postRecordOrderDelayedTask(taskQueue, orderList, 1, 1);
+        postRecordOrderDelayedTask(taskQueue, orderList, 2, 1);
+        postRecordOrderDelayedTask(taskQueue, orderList, 3, 1);
+    }
+
     /**
      * A helper which posts a task on the handler which when run blocks until unblock() is called.
      */
diff --git a/build/branding_value.sh b/build/branding_value.sh
deleted file mode 100755
index 9fcb550..0000000
--- a/build/branding_value.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2008 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.
-
-# This is a wrapper for fetching values from the BRANDING files.  Pass the
-# value of GYP's branding variable followed by the key you want and the right
-# file is checked.
-#
-#  branding_value.sh Chromium COPYRIGHT
-#  branding_value.sh Chromium PRODUCT_FULLNAME
-#
-
-set -e
-
-if [ $# -ne 2 ] ;  then
-  echo "error: expect two arguments, branding and key" >&2
-  exit 1
-fi
-
-BUILD_BRANDING=$1
-THE_KEY=$2
-
-pushd $(dirname "${0}") > /dev/null
-BUILD_DIR=$(pwd)
-popd > /dev/null
-
-TOP="${BUILD_DIR}/.."
-
-case ${BUILD_BRANDING} in
-  Chromium)
-    BRANDING_FILE="${TOP}/chrome/app/theme/chromium/BRANDING"
-    ;;
-  Chrome)
-    BRANDING_FILE="${TOP}/chrome/app/theme/google_chrome/BRANDING"
-    ;;
-  *)
-    echo "error: unknown branding: ${BUILD_BRANDING}" >&2
-    exit 1
-    ;;
-esac
-
-BRANDING_VALUE=$(sed -n -e "s/^${THE_KEY}=\(.*\)\$/\1/p" "${BRANDING_FILE}")
-
-if [ -z "${BRANDING_VALUE}" ] ; then
-  echo "error: failed to find key '${THE_KEY}'" >&2
-  exit 1
-fi
-
-echo "${BRANDING_VALUE}"
diff --git a/build/config/linux/gtk/BUILD.gn b/build/config/linux/gtk/BUILD.gn
index a2b40d82..fe2e106 100644
--- a/build/config/linux/gtk/BUILD.gn
+++ b/build/config/linux/gtk/BUILD.gn
@@ -28,10 +28,10 @@
     "//chrome/test:unit_tests",
     "//examples:peerconnection_client",
     "//gpu/gles2_conform_support:gles2_conform_test_windowless",
-    "//remoting/host",
     "//remoting/host/linux",
     "//remoting/host/it2me:common",
     "//remoting/host/it2me:remote_assistance_host",
+    "//remoting/host:common",
     "//remoting/host:remoting_me2me_host_static",
     "//remoting/test:it2me_standalone_host_main",
     "//webrtc/examples:peerconnection_client",
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 6365a91..68600f49 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -118,7 +118,7 @@
   worklet_animation->UpdateInputState(state.get(), base::TimeTicks::Now(),
                                       scroll_tree, true);
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(worklet_animation_id_.scope_id);
+      state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(1234, input->added_and_updated_animations[0].current_time);
 }
 
@@ -141,18 +141,18 @@
                                       true);
   // First state request sets the start time and thus current time should be 0.
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(worklet_animation_id_.scope_id);
+      state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(0, input->added_and_updated_animations[0].current_time);
   state.reset(new MutatorInputState);
   worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree,
                                       true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(123.4, input->updated_animations[0].current_time);
   // Should always offset from start time.
   state.reset(new MutatorInputState());
   worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree,
                                       true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(246.8, input->updated_animations[0].current_time);
 }
 
@@ -178,7 +178,7 @@
   base::TimeTicks time;
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(worklet_animation_id_.scope_id);
+      state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->added_and_updated_animations.size(), 1u);
   EXPECT_EQ("test_name", input->added_and_updated_animations[0].name);
   EXPECT_EQ(input->updated_animations.size(), 0u);
@@ -189,7 +189,7 @@
   state.reset(new MutatorInputState());
   time += base::TimeDelta::FromSecondsD(0.1);
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
   EXPECT_EQ(input->updated_animations.size(), 1u);
   EXPECT_EQ(input->removed_animations.size(), 0u);
@@ -200,7 +200,7 @@
   state.reset(new MutatorInputState());
   time += base::TimeDelta::FromSecondsD(0.1);
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
   EXPECT_EQ(input->updated_animations.size(), 1u);
   EXPECT_EQ(input->removed_animations.size(), 0u);
@@ -211,7 +211,7 @@
   worklet_animation_->UpdateState(true, nullptr);
   state.reset(new MutatorInputState());
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
   EXPECT_EQ(input->updated_animations.size(), 0u);
   EXPECT_EQ(input->removed_animations.size(), 1u);
@@ -236,21 +236,21 @@
   base::TimeTicks time;
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(worklet_animation_id_.scope_id);
+      state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->added_and_updated_animations.size(), 1u);
   EXPECT_EQ(input->updated_animations.size(), 0u);
 
   state.reset(new MutatorInputState());
   // No update on the input state if input time stays the same.
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_FALSE(input);
 
   state.reset(new MutatorInputState());
   // Different input time causes the input state to be updated.
   time += base::TimeDelta::FromSecondsD(0.1);
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->updated_animations.size(), 1u);
 
   state.reset(new MutatorInputState());
@@ -258,7 +258,7 @@
   // the input time doesn't change.
   worklet_animation_->RemoveKeyframeModel(keyframe_model_id);
   worklet_animation_->UpdateInputState(state.get(), time, scroll_tree, true);
-  input = state->TakeWorkletState(worklet_animation_id_.scope_id);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
   EXPECT_EQ(input->updated_animations.size(), 0u);
   EXPECT_EQ(input->removed_animations.size(), 1u);
 }
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index 9c841f10..2fb5a10 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -2120,7 +2120,8 @@
     // image we've cached.
     EXPECT_TRUE(decoded_image == decoded_draw_image.image());
     // Ensure that the SW decoded image had colorspace conversion applied.
-    EXPECT_TRUE(decoded_image->colorSpace() == target_color_space.get());
+    EXPECT_TRUE(SkColorSpace::Equals(decoded_image->colorSpace(),
+                                     target_color_space.get()));
   }
 
   cache->DrawWithImageFinished(draw_image, decoded_draw_image);
@@ -2169,8 +2170,8 @@
                                      target_color_space.get()));
   } else {
     // Ensure that the HW uploaded image had color space conversion applied.
-    EXPECT_TRUE(decoded_draw_image.image()->colorSpace() ==
-                target_color_space.get());
+    EXPECT_TRUE(SkColorSpace::Equals(decoded_draw_image.image()->colorSpace(),
+                                     target_color_space.get()));
   }
 
   cache->DrawWithImageFinished(draw_image, decoded_draw_image);
diff --git a/cc/trees/layer_tree_mutator.cc b/cc/trees/layer_tree_mutator.cc
index 491eec5..15d2b6a 100644
--- a/cc/trees/layer_tree_mutator.cc
+++ b/cc/trees/layer_tree_mutator.cc
@@ -24,21 +24,22 @@
 AnimationWorkletInput::AddAndUpdateState::~AddAndUpdateState() = default;
 
 #if DCHECK_IS_ON()
-bool AnimationWorkletInput::ValidateScope(int scope_id) const {
+bool AnimationWorkletInput::ValidateId(int worklet_id) const {
   return std::all_of(added_and_updated_animations.cbegin(),
                      added_and_updated_animations.cend(),
-                     [scope_id](auto& it) {
-                       return it.worklet_animation_id.scope_id == scope_id;
+                     [worklet_id](auto& it) {
+                       return it.worklet_animation_id.worklet_id == worklet_id;
                      }) &&
          std::all_of(updated_animations.cbegin(), updated_animations.cend(),
-                     [scope_id](auto& it) {
-                       return it.worklet_animation_id.scope_id == scope_id;
+                     [worklet_id](auto& it) {
+                       return it.worklet_animation_id.worklet_id == worklet_id;
                      }) &&
          std::all_of(
              removed_animations.cbegin(), removed_animations.cend(),
-             [scope_id](auto& it) { return it.scope_id == scope_id; }) &&
-         std::all_of(peeked_animations.cbegin(), peeked_animations.cend(),
-                     [scope_id](auto& it) { return it.scope_id == scope_id; });
+             [worklet_id](auto& it) { return it.worklet_id == worklet_id; }) &&
+         std::all_of(
+             peeked_animations.cbegin(), peeked_animations.cend(),
+             [worklet_id](auto& it) { return it.worklet_id == worklet_id; });
 }
 #endif
 
@@ -65,29 +66,29 @@
 
 void MutatorInputState::Add(AnimationWorkletInput::AddAndUpdateState&& state) {
   AnimationWorkletInput& worklet_input =
-      EnsureWorkletEntry(state.worklet_animation_id.scope_id);
+      EnsureWorkletEntry(state.worklet_animation_id.worklet_id);
   worklet_input.added_and_updated_animations.push_back(std::move(state));
 }
 void MutatorInputState::Update(AnimationWorkletInput::UpdateState&& state) {
   AnimationWorkletInput& worklet_input =
-      EnsureWorkletEntry(state.worklet_animation_id.scope_id);
+      EnsureWorkletEntry(state.worklet_animation_id.worklet_id);
   worklet_input.updated_animations.push_back(std::move(state));
 }
 void MutatorInputState::Remove(WorkletAnimationId worklet_animation_id) {
   AnimationWorkletInput& worklet_input =
-      EnsureWorkletEntry(worklet_animation_id.scope_id);
+      EnsureWorkletEntry(worklet_animation_id.worklet_id);
   worklet_input.removed_animations.push_back(worklet_animation_id);
 }
 
 void MutatorInputState::Peek(WorkletAnimationId worklet_animation_id) {
   AnimationWorkletInput& worklet_input =
-      EnsureWorkletEntry(worklet_animation_id.scope_id);
+      EnsureWorkletEntry(worklet_animation_id.worklet_id);
   worklet_input.peeked_animations.push_back(worklet_animation_id);
 }
 
 std::unique_ptr<AnimationWorkletInput> MutatorInputState::TakeWorkletState(
-    int scope_id) {
-  auto it = inputs_.find(scope_id);
+    int worklet_id) {
+  auto it = inputs_.find(worklet_id);
   if (it == inputs_.end())
     return nullptr;
 
diff --git a/cc/trees/layer_tree_mutator.h b/cc/trees/layer_tree_mutator.h
index 42cc3177..c3d6fbe 100644
--- a/cc/trees/layer_tree_mutator.h
+++ b/cc/trees/layer_tree_mutator.h
@@ -21,13 +21,13 @@
 struct CC_EXPORT WorkletAnimationId {
   // Uniquely identifies the animation worklet with which this animation is
   // associated.
-  int scope_id;
+  int worklet_id;
   // Uniquely identifies the animation within its animation worklet. Note that
   // animation_id is only guaranteed to be unique per animation worklet.
   int animation_id;
 
   inline bool operator==(const WorkletAnimationId& rhs) const {
-    return (this->scope_id == rhs.scope_id) &&
+    return (this->worklet_id == rhs.worklet_id) &&
            (this->animation_id == rhs.animation_id);
   }
 };
@@ -68,8 +68,8 @@
   ~AnimationWorkletInput();
 
 #if DCHECK_IS_ON()
-  // Verifies all animation states have the expected scope id.
-  bool ValidateScope(int scope_id) const;
+  // Verifies all animation states have the expected worklet id.
+  bool ValidateId(int worklet_id) const;
 #endif
   DISALLOW_COPY_AND_ASSIGN(AnimationWorkletInput);
 };
diff --git a/chrome/android/java/monochrome_public_apk.proguard_flags.expected b/chrome/android/java/monochrome_public_apk.proguard_flags.expected
index 43076576..9241b68d 100644
--- a/chrome/android/java/monochrome_public_apk.proguard_flags.expected
+++ b/chrome/android/java/monochrome_public_apk.proguard_flags.expected
@@ -246,6 +246,18 @@
 }
 
 ################################################################################
+# ../../base/android/proguard/dcheck_is_off.flags
+################################################################################
+# 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.
+
+# Contains flags that are applied only when ENABLE_DCHECK=false.
+
+# Ensure that GcStateAssert is fully optimized away.
+-checkdiscard class org.chromium.base.GcStateAssert$WrappedReference { *; }
+
+################################################################################
 # ../../base/android/proguard/enable_obfuscation.flags
 ################################################################################
 # Copyright 2016 The Chromium Authors. All rights reserved.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 2a78d9b..50933e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -211,6 +211,7 @@
     public static final String DOWNLOAD_PROGRESS_INFOBAR = "DownloadProgressInfoBar";
     public static final String DOWNLOAD_HOME_V2 = "DownloadHomeV2";
     public static final String DOWNLOADS_FOREGROUND = "DownloadsForeground";
+    public static final String DOWNLOADS_AUTO_RESUMPTION_NATIVE = "DownloadsAutoResumptionNative";
     public static final String DOWNLOADS_LOCATION_CHANGE = "DownloadsLocationChange";
     public static final String EPHEMERAL_TAB = "EphemeralTab";
     public static final String EXPERIMENTAL_APP_BANNERS = "ExperimentalAppBanners";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java
index 4acce6c6..63cf396 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java
@@ -166,7 +166,7 @@
         String packageName =
                 CustomTabsConnection.getInstance().getClientPackageNameForSession(session);
         if (TextUtils.isEmpty(packageName)) return false;
-        boolean valid = OriginVerifier.wasPreviouslyVerified(
+        boolean valid = OriginVerifier.isValidOrigin(
                 packageName, new Origin(referrer), CustomTabsService.RELATION_USE_AS_ORIGIN);
 
         // OriginVerifier should only be allowing https schemes.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
index 5b5c428..1b54e42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
@@ -64,7 +64,7 @@
 
         // We expect that origin has been verified on the client side, and here we synchronously
         // check if a result of a successful verification has been cached.
-        return OriginVerifier.wasPreviouslyVerified(clientPackageName, origin,
+        return OriginVerifier.isValidOrigin(clientPackageName, origin,
                 CustomTabsService.RELATION_HANDLE_ALL_URLS);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
index 03285ee..a91000d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java
@@ -36,11 +36,7 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.util.Collections;
 import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Used to verify postMessage origin for a designated package name.
@@ -59,20 +55,13 @@
     private static final String USE_AS_ORIGIN = "delegate_permission/common.use_as_origin";
     private static final String HANDLE_ALL_URLS = "delegate_permission/common.handle_all_urls";
 
+    private final OriginVerificationListener mListener;
     private final String mPackageName;
     private final String mSignatureFingerprint;
     private final @Relation int mRelation;
     private long mNativeOriginVerifier;
-    private OriginVerificationListener mListener;
     private Origin mOrigin;
 
-    /**
-     * A collection of Relationships (stored as Strings, with the signature set to an empty String)
-     * that we override verifications to succeed for. It is threadsafe.
-     */
-    private static final AtomicReference<Set<String>> sVerificationOverrides =
-            new AtomicReference<>();
-
     /** Small helper class to post a result of origin verification. */
     private class VerifiedCallback implements Runnable {
         private final boolean mResult;
@@ -102,61 +91,33 @@
     }
 
     /**
-     * Ensures that subsequent calls to {@link OriginVerifier#start} result in a success without
-     * performing the full check.
+     * Mark an origin as verified for a package.
+     * @param packageName The package name to prepopulate for.
+     * @param origin The origin to add as verified.
+     * @param relation The Digital Asset Links relation verified.
      */
-    public static void addVerificationOverride(String packageName, Origin origin,
-            int relationship) {
-        if (sVerificationOverrides.get() == null) {
-            sVerificationOverrides.compareAndSet(null,
-                    Collections.newSetFromMap(new ConcurrentHashMap<>()));
-        }
-        sVerificationOverrides.get().add(
-                new Relationship(packageName, "", origin, relationship).toString());
-    }
+    public static void addVerifiedOriginForPackage(
+            String packageName, Origin origin, @Relation int relation) {
+        Log.d(TAG, "Adding: %s for %s", packageName, origin);
+        VerificationResultStore.addRelationship(new Relationship(packageName, origin, relation));
 
-    /**
-     * Checks whether the origin was verified for that origin with a call to {@link #start}.
-     */
-    public boolean wasPreviouslyVerified(Origin origin) {
-        return wasPreviouslyVerified(mPackageName, mSignatureFingerprint, origin, mRelation);
+        TrustedWebActivityClient.registerClient(ContextUtils.getApplicationContext(),
+                origin, packageName);
     }
 
     /**
      * Returns whether an origin is first-party relative to a given package name.
      *
-     * This only returns data from previously cached relations, and does not trigger an asynchronous
-     * validation. This cache is persisted across Chrome restarts. If you have an instance of
-     * OriginVerifier, use {@link #wasPreviouslyVerified(Origin)} instead as that avoids recomputing
-     * the signatureFingerprint of the package.
+     * This only returns data from previously cached relations, and does not
+     * trigger an asynchronous validation.
      *
-     * @param packageName The package name.
-     * @param origin The origin to verify.
+     * @param packageName The package name
+     * @param origin The origin to verify
      * @param relation The Digital Asset Links relation to verify for.
      */
-    public static boolean wasPreviouslyVerified(String packageName, Origin origin,
-            @Relation int relation) {
-        return shouldOverrideVerification(packageName, origin, relation)
-                || VerificationResultStore.isRelationshipSaved(new Relationship(packageName,
-                getCertificateSHA256FingerprintForPackage(packageName), origin, relation));
-    }
-
-
-    /**
-     * Returns whether an origin is first-party relative to a given package name.
-     *
-     * This only returns data from previously cached relations, and does not trigger an asynchronous
-     * validation. This cache is persisted across Chrome restarts.
-     *
-     * @param packageName The package name.
-     * @param signatureFingerprint The signature of the package.
-     * @param origin The origin to verify.
-     * @param relation The Digital Asset Links relation to verify for.
-     */
-    private static boolean wasPreviouslyVerified(String packageName, String signatureFingerprint,
-            Origin origin, @Relation int relation) {
+    public static boolean isValidOrigin(String packageName, Origin origin, @Relation int relation) {
         return VerificationResultStore.isRelationshipSaved(
-                new Relationship(packageName, signatureFingerprint, origin, relation));
+                new Relationship(packageName, origin, relation));
     }
 
     /**
@@ -178,11 +139,14 @@
 
     /**
      * Main constructor.
-     * Use {@link OriginVerifier#start}
+     * Use {@link OriginVerifier#start(Origin)}
+     * @param listener The listener who will get the verification result.
      * @param packageName The package for the Android application for verification.
      * @param relation Digital Asset Links {@link Relation} to use during verification.
      */
-    public OriginVerifier(String packageName, @Relation int relation) {
+    public OriginVerifier(
+            OriginVerificationListener listener, String packageName, @Relation int relation) {
+        mListener = listener;
         mPackageName = packageName;
         mSignatureFingerprint = getCertificateSHA256FingerprintForPackage(mPackageName);
         mRelation = relation;
@@ -193,12 +157,10 @@
      * making a network request for non-cached origins with a URLFetcher using the last used
      * profile as context.
      * @param origin The postMessage origin the application is claiming to have. Can't be null.
-     * @param listener The listener who will get the verification result.
      */
-    public void start(@NonNull OriginVerificationListener listener, @NonNull Origin origin) {
+    public void start(@NonNull Origin origin) {
         ThreadUtils.assertOnUiThread();
         mOrigin = origin;
-        mListener = listener;
 
         // Website to app Digital Asset Link verification can be skipped for a specific URL by
         // passing a command line flag to ease development.
@@ -221,12 +183,14 @@
             return;
         }
 
-        if (shouldOverrideVerification(mPackageName, mOrigin, mRelation)) {
-            Log.i(TAG, "Verification succeeded for %s, it was overridden.", origin);
+        // If this origin is cached as verified already, use that.
+        if (isValidOrigin(mPackageName, origin, mRelation)) {
+            Log.i(TAG, "Verification succeeded for %s, it was cached.", origin);
+            BrowserServicesMetrics.recordVerificationResult(
+                    BrowserServicesMetrics.VerificationResult.CACHED_SUCCESS);
             ThreadUtils.runOnUiThread(new VerifiedCallback(true, null));
             return;
         }
-
         if (mNativeOriginVerifier != 0) cleanUp();
         if (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
                         .isStartupSuccessfullyCompleted()) {
@@ -257,14 +221,6 @@
         }
     }
 
-    private static boolean shouldOverrideVerification(String packageName, Origin origin,
-            int relation) {
-        if (sVerificationOverrides.get() == null) return false;
-
-        return sVerificationOverrides.get().contains(
-                new Relationship(packageName, "", origin, relation).toString());
-    }
-
     /**
      * Cleanup native dependencies on this object.
      */
@@ -300,10 +256,12 @@
         if (packageInfo == null) return null;
 
         InputStream input = new ByteArrayInputStream(packageInfo.signatures[0].toByteArray());
+        X509Certificate certificate = null;
         String hexString = null;
         try {
-            X509Certificate certificate = (X509Certificate)
-                    CertificateFactory.getInstance("X509").generateCertificate(input);
+            certificate =
+                    (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(
+                            input);
             hexString = byteArrayToHexString(
                     MessageDigest.getInstance("SHA256").digest(certificate.getEncoded()));
         } catch (CertificateEncodingException e) {
@@ -356,12 +314,7 @@
     private void originVerified(boolean originVerified, Boolean online) {
         Log.i(TAG, "Verification %s.", (originVerified ? "succeeded" : "failed"));
         if (originVerified) {
-            Log.d(TAG, "Adding: %s for %s", mPackageName, mOrigin);
-            VerificationResultStore.addRelationship(new Relationship(mPackageName,
-                    mSignatureFingerprint, mOrigin, mRelation));
-
-            TrustedWebActivityClient.registerClient(ContextUtils.getApplicationContext(),
-                    mOrigin, mPackageName);
+            addVerifiedOriginForPackage(mPackageName, mOrigin, mRelation);
         }
 
         // We save the result even if there is a failure as a way of overwriting a previously
@@ -378,8 +331,7 @@
      * Saves the result of a verification to Preferences so we can reuse it when offline.
      */
     private void saveVerificationResult(boolean originVerified) {
-        Relationship relationship =
-                new Relationship(mPackageName, mSignatureFingerprint, mOrigin, mRelation);
+        Relationship relationship = new Relationship(mPackageName, mOrigin, mRelation);
         if (originVerified) {
             VerificationResultStore.addRelationship(relationship);
         } else {
@@ -393,7 +345,7 @@
     private void checkForSavedResult() {
         try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
             boolean verified = VerificationResultStore.isRelationshipSaved(
-                    new Relationship(mPackageName, mSignatureFingerprint, mOrigin, mRelation));
+                    new Relationship(mPackageName, mOrigin, mRelation));
 
             BrowserServicesMetrics.recordVerificationResult(verified
                             ? BrowserServicesMetrics.VerificationResult.OFFLINE_SUCCESS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/Relationship.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/Relationship.java
index 674bfd5..d713bc2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/Relationship.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/Relationship.java
@@ -13,13 +13,10 @@
     public final String packageName;
     public final Origin origin;
     public final int relation;
-    public final String signatureFingerprint;
 
     /** Creates a {@link Relationship} to hold relationship details. */
-    public Relationship(String packageName, String signatureFingerprint, Origin origin,
-            int relation) {
+    public Relationship(String packageName, Origin origin, int relation) {
         this.packageName = packageName;
-        this.signatureFingerprint = signatureFingerprint;
         this.origin = origin;
         this.relation = relation;
     }
@@ -31,6 +28,6 @@
     @Override
     public String toString() {
         // Neither package names nor origins contain commas.
-        return packageName + "," + origin + "," + relation + "," + signatureFingerprint;
+        return packageName + "," + origin + "," + relation;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
index 5e55db75..3fdedd6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/controller/TrustedWebActivityVerifier.java
@@ -46,7 +46,6 @@
     private final ActivityTabProvider mActivityTabProvider;
     private final TabObserverRegistrar mTabObserverRegistrar;
     private final String mClientPackageName;
-    private final OriginVerifier mOriginVerifier;
 
     @Nullable private VerificationState mState;
 
@@ -87,7 +86,10 @@
 
             // This doesn't perform a network request or attempt new verification - it checks to
             // see if a verification already exists for the given inputs.
-            handleVerificationResult(isPageOnVerifiedOrigin(url), new Origin(url));
+            Origin origin = new Origin(url);
+            boolean verified = OriginVerifier.isValidOrigin(mClientPackageName, origin,
+                    RELATIONSHIP);
+            handleVerificationResult(verified, origin);
         }
     };
 
@@ -107,8 +109,6 @@
                 intentDataProvider.getSession());
         assert mClientPackageName != null;
 
-        mOriginVerifier = new OriginVerifier(mClientPackageName, RELATIONSHIP);
-
         tabObserverRegistrar.registerTabObserver(mVerifyOnPageLoadObserver);
         lifecycleDispatcher.register(this);
     }
@@ -152,7 +152,7 @@
 
     /** Returns whether the given |url| is on an Origin that the package has been verified for. */
     public boolean isPageOnVerifiedOrigin(String url) {
-        return mOriginVerifier.wasPreviouslyVerified(new Origin(url));
+        return OriginVerifier.isValidOrigin(mClientPackageName, new Origin(url), RELATIONSHIP);
     }
 
     /**
@@ -167,11 +167,12 @@
         }
 
         updateState(origin, VERIFICATION_PENDING);
-        mOriginVerifier.start((packageName2, origin2, verified, online) -> {
+
+        new OriginVerifier((packageName2, origin2, verified, online) -> {
             if (!origin.equals(new Origin(tab.getUrl()))) return;
 
             handleVerificationResult(verified, origin);
-        }, origin);
+        }, mClientPackageName, RELATIONSHIP).start(origin);
     }
 
     private void handleVerificationResult(boolean verified, Origin origin) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactView.java
index 564de3c..2883c05e38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactView.java
@@ -67,6 +67,7 @@
 
         mDisplayName = findViewById(R.id.title);
         mDetailsView = findViewById(R.id.description);
+        mDetailsView.setMaxLines(2);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
index 7e2b413c4..94096d23 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -476,8 +476,8 @@
             }
         };
 
-        params.originVerifier = new OriginVerifier(params.getPackageName(), relation);
-        ThreadUtils.runOnUiThread(() -> { params.originVerifier.start(listener, origin); });
+        params.originVerifier = new OriginVerifier(listener, params.getPackageName(), relation);
+        ThreadUtils.runOnUiThread(() -> { params.originVerifier.start(origin); });
         if (relation == CustomTabsService.RELATION_HANDLE_ALL_URLS
                 && InstalledAppProviderImpl.isAppInstalledAndAssociatedWithOrigin(
                            params.getPackageName(), URI.create(origin.toString()),
@@ -704,7 +704,7 @@
      */
     public synchronized boolean isFirstPartyOriginForSession(
             CustomTabsSessionToken session, Origin origin) {
-        return OriginVerifier.wasPreviouslyVerified(getClientPackageNameForSession(session), origin,
+        return OriginVerifier.isValidOrigin(getClientPackageNameForSession(session), origin,
                 CustomTabsService.RELATION_USE_AS_ORIGIN);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java b/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java
index 1662f930..81016d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java
@@ -6,8 +6,8 @@
 
 import android.database.AbstractCursor;
 import android.database.CursorWindow;
-import android.util.Log;
 
+import org.chromium.base.GcStateAssert;
 import org.chromium.base.annotations.CalledByNative;
 
 import java.sql.Types;
@@ -33,6 +33,8 @@
     private final Object mMoveLock = new Object();
     private final Object mGetBlobLock = new Object();
 
+    private final GcStateAssert mGcStateAssert = GcStateAssert.create(this);
+
     private SQLiteCursor(long nativeSQLiteCursor) {
         mNativeSQLiteCursor = nativeSQLiteCursor;
     }
@@ -97,6 +99,7 @@
             if (mNativeSQLiteCursor != 0) {
                 nativeDestroy(mNativeSQLiteCursor);
                 mNativeSQLiteCursor = 0;
+                GcStateAssert.setSafeToGc(mGcStateAssert, true);
             }
         }
     }
@@ -122,15 +125,6 @@
     }
 
     @Override
-    protected void finalize() {
-        super.finalize();
-        if (!isClosed()) {
-            Log.w(TAG, "Cursor hasn't been closed");
-            close();
-        }
-    }
-
-    @Override
     public void fillWindow(int position, CursorWindow window) {
         if (position < 0 || position > getCount()) {
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index b63b97fc..17fc3cf6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -361,6 +361,7 @@
         }
         updateDownloadProgress(item, status);
 
+        if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
         DownloadProgress progress = mDownloadProgressMap.get(item.getId());
         if (progress == null) return;
         if (!isAutoResumable || sIsNetworkListenerDisabled) return;
@@ -1321,6 +1322,7 @@
      * @param guid Id of the download item.
      */
     private void addAutoResumableDownload(String guid) {
+        if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
         if (mAutoResumableDownloadIds.isEmpty() && !sIsNetworkListenerDisabled) {
             mNetworkChangeNotifier = new NetworkChangeNotifierAutoDetect(
                     this, new RegistrationPolicyAlwaysRegister());
@@ -1335,6 +1337,7 @@
      * @param guid Id of the download item.
      */
     private void removeAutoResumableDownload(String guid) {
+        if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
         if (mAutoResumableDownloadIds.isEmpty()) return;
         mAutoResumableDownloadIds.remove(guid);
         stopListenToConnectionChangeIfNotNeeded();
@@ -1352,6 +1355,7 @@
 
     @Override
     public void onConnectionTypeChanged(int connectionType) {
+        if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
         if (mAutoResumableDownloadIds.isEmpty()) return;
         if (connectionType == ConnectionType.CONNECTION_NONE) return;
         boolean isMetered = isActiveNetworkMetered(ContextUtils.getApplicationContext());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 609f648..599f93f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -31,6 +31,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.FailState;
 import org.chromium.components.offline_items_collection.LegacyHelpers;
@@ -528,6 +529,8 @@
      * already in progress, do nothing.
      */
     void resumeAllPendingDownloads() {
+        if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
+
         // Limit the number of auto resumption attempts in case Chrome falls into a vicious cycle.
         DownloadResumptionScheduler.getDownloadResumptionScheduler().cancel();
         int numAutoResumptionAtemptLeft = getResumptionAttemptLeft();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java
index e55b7f8..9cf6ce6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadResumptionScheduler.java
@@ -7,6 +7,7 @@
 import android.annotation.SuppressLint;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.background_task_scheduler.TaskInfo;
@@ -36,6 +37,8 @@
      * if there are resumable downloads available.
      */
     public void scheduleIfNecessary() {
+        if (FeatureUtilities.isDownloadAutoResumptionEnabledInNative()) return;
+
         List<DownloadSharedPreferenceEntry> entries =
                 DownloadSharedPreferenceHelper.getInstance().getEntries();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
index a489f1f..03cee93 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -9,6 +9,7 @@
 import android.view.ViewGroup;
 import android.view.Window;
 
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.WindowDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
@@ -84,8 +85,10 @@
      * Initialize controls that will act as hooks to various functions.
      * @param windowDelegate {@link WindowDelegate} that will provide {@link Window} related info.
      * @param windowAndroid {@link WindowAndroid} that is used by the owning {@link Activity}.
+     * @param provider An {@link ActivityTabProvider} to access the activity's current tab.
      */
-    void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid);
+    void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid,
+            ActivityTabProvider provider);
 
     /**
      * Adds a URL focus change listener that will be notified when the URL gains or loses focus.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index f149575..9cfedb5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -31,6 +31,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.WindowDelegate;
@@ -247,12 +248,14 @@
     }
 
     @Override
-    public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid) {
+    public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid,
+            ActivityTabProvider provider) {
         mWindowDelegate = windowDelegate;
         mWindowAndroid = windowAndroid;
 
         mUrlCoordinator.setWindowDelegate(windowDelegate);
         mAutocompleteCoordinator.setWindowAndroid(windowAndroid);
+        mAutocompleteCoordinator.setActivityTabProvider(provider);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 14de9d3..1b0744d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -11,6 +11,7 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.components.security_state.ConnectionSecurityLevel;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /**
@@ -32,6 +33,8 @@
     private int mSeparatorMinWidth;
     private int mVerboseStatusTextMinWidth;
 
+    private @ConnectionSecurityLevel int mPageSecurityLevel;
+
     private @DrawableRes int mSecurityIconRes;
     private @DrawableRes int mSecurityIconTintRes;
     private @StringRes int mSecurityIconDescriptionRes;
@@ -73,6 +76,16 @@
     }
 
     /**
+     * Specify displayed page's security level.
+     */
+    void setPageSecurityLevel(@ConnectionSecurityLevel int level) {
+        if (mPageSecurityLevel == level) return;
+        mPageSecurityLevel = level;
+        updateStatusVisibility();
+        updateLocationBarIcon();
+    }
+
+    /**
      * Specify icon displayed by the security chip.
      */
     void setSecurityIconResource(@DrawableRes int securityIcon) {
@@ -179,14 +192,6 @@
     }
 
     /**
-     * Specify whether parent allows verbose status text.
-     */
-    void setVerboseStatusTextAllowed(boolean isVerboseStatusTextAllowed) {
-        mVerboseStatusAllowed = isVerboseStatusTextAllowed;
-        updateStatusVisibility();
-    }
-
-    /**
      * Specify minimum width of the verbose status text field.
      */
     void setVerboseStatusTextMinWidth(int width) {
@@ -197,7 +202,6 @@
      * Update visibility of the verbose status text field.
      */
     private void updateStatusVisibility() {
-        @StringRes
         int statusText = 0;
 
         if (mPageIsPreview) {
@@ -207,7 +211,7 @@
         }
 
         // Decide whether presenting verbose status text makes sense.
-        boolean newVisibility = mVerboseStatusAllowed && mVerboseStatusSpaceAvailable
+        boolean newVisibility = shouldShowVerboseStatusText() && mVerboseStatusSpaceAvailable
                 && (!mUrlHasFocus) && (statusText != 0);
 
         // Update status content only if it is visible.
@@ -258,6 +262,14 @@
     }
 
     /**
+     * Compute verbose status text for the current page.
+     */
+    private boolean shouldShowVerboseStatusText() {
+        return (mPageIsPreview && mPageSecurityLevel != ConnectionSecurityLevel.DANGEROUS)
+                || mPageIsOffline;
+    }
+
+    /**
      * Update selection of icon presented on the location bar.
      *
      * - Navigation button is:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
index 07b0013..fa305eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusViewCoordinator.java
@@ -139,7 +139,7 @@
     private void updateVerboseStatusVisibility() {
         // TODO(ender): turn around logic for ToolbarDataProvider to offer
         // notifications rather than polling for these attributes.
-        mMediator.setVerboseStatusTextAllowed(mToolbarDataProvider.shouldShowVerboseStatus());
+        mMediator.setPageSecurityLevel(mToolbarDataProvider.getSecurityLevel());
         mMediator.setPageIsOffline(mToolbarDataProvider.isOfflinePage());
         mMediator.setPageIsPreview(mToolbarDataProvider.isPreview());
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 164915e..d11f8f3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -17,6 +17,7 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlTextChangeListener;
 import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider;
@@ -26,7 +27,8 @@
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionListViewBinder.SuggestionListViewHolder;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionView;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder;
-import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionCoordinator;
+import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor;
+import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionViewBinder;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
@@ -89,7 +91,7 @@
     /**
      * Provides the additional functionality to trigger and interact with autocomplete suggestions.
      */
-    public interface AutocompleteDelegate extends EditUrlSuggestionCoordinator.LocationBarDelegate {
+    public interface AutocompleteDelegate extends EditUrlSuggestionProcessor.LocationBarDelegate {
         /**
          * Notified that the URL text has changed.
          */
@@ -188,6 +190,11 @@
                         () -> new SuggestionView(mListView.getContext()),
                         SuggestionViewViewBinder::bind);
 
+                adapter.registerType(
+                        OmniboxSuggestionUiType.EDIT_URL_SUGGESTION,
+                        () -> EditUrlSuggestionProcessor.createView(mListView.getContext()),
+                        EditUrlSuggestionViewBinder::bind);
+
                 mHolder = new SuggestionListViewHolder(container, list, adapter);
 
                 for (int i = 0; i < mCallbacks.size(); i++) {
@@ -241,6 +248,13 @@
     }
 
     /**
+     * @param provider A means of accessing the activity's tab.
+     */
+    public void setActivityTabProvider(ActivityTabProvider provider) {
+        mMediator.setActivityTabProvider(provider);
+    }
+
+    /**
      * Whether omnibox autocomplete should currently be prevented from generating suggestions.
      */
     public void setShouldPreventOmniboxAutocomplete(boolean prevent) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index 6bdbb5d..76b1bc2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -20,6 +20,8 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
@@ -30,6 +32,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.BasicSuggestionProcessor.SuggestionHost;
 import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionView.SuggestionViewDelegate;
+import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.content_public.browser.WebContents;
@@ -83,6 +86,7 @@
     private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>();
     private final Handler mHandler;
     private final BasicSuggestionProcessor mBasicSuggestionProcessor;
+    private EditUrlSuggestionProcessor mEditUrlProcessor;
 
     private ToolbarDataProvider mDataProvider;
     private boolean mNativeInitialized;
@@ -139,6 +143,8 @@
         mAutocomplete = new AutocompleteController(this);
         mHandler = new Handler();
         mBasicSuggestionProcessor = new BasicSuggestionProcessor(mContext, this, textProvider);
+        mEditUrlProcessor = new EditUrlSuggestionProcessor(
+                delegate, (suggestion) -> onSelection(suggestion, 0));
     }
 
     /**
@@ -245,6 +251,13 @@
     void onNativeInitialized() {
         mNativeInitialized = true;
 
+        // The feature is instantiated in the constructor to simplify plumbing. If the feature is
+        // actually disabled, null out the coordinator.
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.SEARCH_READY_OMNIBOX)) {
+            mEditUrlProcessor.destroy();
+            mEditUrlProcessor = null;
+        }
+
         for (Runnable deferredRunnable : mDeferredNativeRunnables) {
             mHandler.post(deferredRunnable);
         }
@@ -252,6 +265,13 @@
         mBasicSuggestionProcessor.onNativeInitialized();
     }
 
+    /**
+     * @param provider A means of accessing the activity tab.
+     */
+    void setActivityTabProvider(ActivityTabProvider provider) {
+        if (mEditUrlProcessor != null) mEditUrlProcessor.setActivityTabProvider(provider);
+    }
+
     /** @see org.chromium.chrome.browser.omnibox.UrlFocusChangeListener#onUrlFocusChange(boolean) */
     void onUrlFocusChange(boolean hasFocus) {
         if (hasFocus) {
@@ -274,6 +294,7 @@
             hideSuggestions();
         }
         mBasicSuggestionProcessor.onUrlFocusChange(hasFocus);
+        if (mEditUrlProcessor != null) mEditUrlProcessor.onUrlFocusChange(hasFocus);
     }
 
     /**
@@ -621,6 +642,9 @@
      * @return The appropriate suggestion processor for the provided suggestion.
      */
     private SuggestionProcessor getProcessorForSuggestion(OmniboxSuggestion suggestion) {
+        if (mEditUrlProcessor != null && mEditUrlProcessor.doesProcessSuggestion(suggestion)) {
+            return mEditUrlProcessor;
+        }
         return mBasicSuggestionProcessor;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
index 77f6f11..0a7b049 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/BasicSuggestionProcessor.java
@@ -184,22 +184,49 @@
         if (numAnswerLines == -1) numAnswerLines = 1;
         model.set(SuggestionViewProperties.IS_ANSWER, true);
 
-        model.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_SP,
-                        (float) AnswerTextBuilder.getMaxTextHeightSp(firstLine)));
-        model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT,
-                new SuggestionTextContainer(AnswerTextBuilder.buildSpannable(firstLine, density)));
+        if (mEnableNewAnswerLayout) {
+            model.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
+                    Pair.create(TypedValue.COMPLEX_UNIT_SP,
+                            (float) AnswerTextBuilder.getMaxTextHeightSp(firstLine)));
+            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT,
+                    new SuggestionTextContainer(
+                            AnswerTextBuilder.buildSpannable(firstLine, density)));
 
-        model.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
-                Pair.create(TypedValue.COMPLEX_UNIT_SP,
-                        (float) AnswerTextBuilder.getMaxTextHeightSp(secondLine)));
-        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT,
-                new SuggestionTextContainer(AnswerTextBuilder.buildSpannable(secondLine, density)));
-        model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, numAnswerLines);
-        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR,
-                SuggestionViewViewBinder.getStandardFontColor(
-                        mContext, model.get(SuggestionCommonProperties.USE_DARK_COLORS)));
-        model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION, View.TEXT_DIRECTION_INHERIT);
+            model.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
+                    Pair.create(TypedValue.COMPLEX_UNIT_SP,
+                            (float) AnswerTextBuilder.getMaxTextHeightSp(secondLine)));
+            model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT,
+                    new SuggestionTextContainer(
+                            AnswerTextBuilder.buildSpannable(secondLine, density)));
+            model.set(SuggestionViewProperties.TEXT_LINE_1_MAX_LINES, numAnswerLines);
+            model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, 1);
+            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR,
+                    SuggestionViewViewBinder.getStandardFontColor(
+                            mContext, model.get(SuggestionCommonProperties.USE_DARK_COLORS)));
+            model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT_DIRECTION,
+                    View.TEXT_DIRECTION_INHERIT);
+        } else {
+            model.set(SuggestionViewProperties.TEXT_LINE_1_SIZING,
+                    Pair.create(TypedValue.COMPLEX_UNIT_SP,
+                            (float) AnswerTextBuilder.getMaxTextHeightSp(firstLine)));
+            model.set(SuggestionViewProperties.TEXT_LINE_1_TEXT,
+                    new SuggestionTextContainer(
+                            AnswerTextBuilder.buildSpannable(firstLine, density)));
+
+            model.set(SuggestionViewProperties.TEXT_LINE_2_SIZING,
+                    Pair.create(TypedValue.COMPLEX_UNIT_SP,
+                            (float) AnswerTextBuilder.getMaxTextHeightSp(secondLine)));
+            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT,
+                    new SuggestionTextContainer(
+                            AnswerTextBuilder.buildSpannable(secondLine, density)));
+            model.set(SuggestionViewProperties.TEXT_LINE_1_MAX_LINES, 1);
+            model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, numAnswerLines);
+            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR,
+                    SuggestionViewViewBinder.getStandardFontColor(
+                            mContext, model.get(SuggestionCommonProperties.USE_DARK_COLORS)));
+            model.set(SuggestionViewProperties.TEXT_LINE_2_TEXT_DIRECTION,
+                    View.TEXT_DIRECTION_INHERIT);
+        }
 
         model.set(SuggestionViewProperties.HAS_ANSWER_IMAGE, secondLine.hasImage());
 
@@ -312,6 +339,7 @@
                         mContext.getResources().getDimension(
                                 org.chromium.chrome.R.dimen
                                         .omnibox_suggestion_second_line_text_size)));
+        model.set(SuggestionViewProperties.TEXT_LINE_1_MAX_LINES, 1);
         model.set(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES, 1);
 
         boolean sameAsTyped =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
index 4a12498..6040315 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java
@@ -360,7 +360,7 @@
         if (!mContentsView.mAllowTint || mContentsView.mSuggestionIcon == null) return;
         DrawableCompat.setTint(mContentsView.mSuggestionIcon,
                 ApiCompatibilityUtils.getColor(getContext().getResources(),
-                useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint));
+                        useDarkTint ? R.color.dark_mode_tint : R.color.white_mode_tint));
         mContentsView.invalidate();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
index 5c79d2f..233ab82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java
@@ -128,6 +128,14 @@
      */
     public static final WritableObjectPropertyKey<Pair<Integer, Float>> TEXT_LINE_1_SIZING =
             new WritableObjectPropertyKey<>();
+    /** The maximum number of lines to be shown for the first line of text. */
+    public static final WritableIntPropertyKey TEXT_LINE_1_MAX_LINES = new WritableIntPropertyKey();
+    /** The color to be applied to the first line of text. */
+    public static final WritableIntPropertyKey TEXT_LINE_1_TEXT_COLOR =
+            new WritableIntPropertyKey();
+    /** The direction the text should be laid out for the first line of text. */
+    public static final WritableIntPropertyKey TEXT_LINE_1_TEXT_DIRECTION =
+            new WritableIntPropertyKey();
     /** The actual text content for the first line of text. */
     public static final WritableObjectPropertyKey<SuggestionTextContainer> TEXT_LINE_1_TEXT =
             new WritableObjectPropertyKey<>();
@@ -154,6 +162,7 @@
 
     public static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {DELEGATE, IS_ANSWER,
             HAS_ANSWER_IMAGE, ANSWER_IMAGE, REFINABLE, SUGGESTION_ICON_TYPE, TEXT_LINE_1_SIZING,
+            TEXT_LINE_1_MAX_LINES, TEXT_LINE_1_TEXT_COLOR, TEXT_LINE_1_TEXT_DIRECTION,
             TEXT_LINE_1_TEXT, TEXT_LINE_2_SIZING, TEXT_LINE_2_MAX_LINES, TEXT_LINE_2_TEXT_COLOR,
             TEXT_LINE_2_TEXT_DIRECTION, TEXT_LINE_2_TEXT};
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java
index b793ad3..2a8db99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java
@@ -12,6 +12,7 @@
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.View;
+import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
@@ -127,6 +128,16 @@
         } else if (SuggestionViewProperties.TEXT_LINE_1_SIZING.equals(propertyKey)) {
             Pair<Integer, Float> sizing = model.get(SuggestionViewProperties.TEXT_LINE_1_SIZING);
             view.getTextLine1().setTextSize(sizing.first, sizing.second);
+        } else if (SuggestionViewProperties.TEXT_LINE_1_MAX_LINES.equals(propertyKey)) {
+            updateSuggestionLayoutType(view, model);
+            updateSuggestionViewTextMaxLines(
+                    view.getTextLine1(), model.get(SuggestionViewProperties.TEXT_LINE_1_MAX_LINES));
+        } else if (SuggestionViewProperties.TEXT_LINE_1_TEXT_COLOR.equals(propertyKey)) {
+            view.getTextLine1().setTextColor(
+                    model.get(SuggestionViewProperties.TEXT_LINE_1_TEXT_COLOR));
+        } else if (SuggestionViewProperties.TEXT_LINE_1_TEXT_DIRECTION.equals(propertyKey)) {
+            ApiCompatibilityUtils.setTextDirection(view.getTextLine1(),
+                    model.get(SuggestionViewProperties.TEXT_LINE_1_TEXT_DIRECTION));
         } else if (SuggestionViewProperties.TEXT_LINE_1_TEXT.equals(propertyKey)) {
             view.getTextLine1().setText(model.get(SuggestionViewProperties.TEXT_LINE_1_TEXT).text);
         } else if (SuggestionViewProperties.TEXT_LINE_2_SIZING.equals(propertyKey)) {
@@ -134,15 +145,8 @@
             view.getTextLine2().setTextSize(sizing.first, sizing.second);
         } else if (SuggestionViewProperties.TEXT_LINE_2_MAX_LINES.equals(propertyKey)) {
             updateSuggestionLayoutType(view, model);
-            int numberLines = model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES);
-            if (numberLines == 1) {
-                view.getTextLine2().setEllipsize(null);
-                view.getTextLine2().setSingleLine();
-            } else {
-                view.getTextLine2().setSingleLine(false);
-                view.getTextLine2().setEllipsize(TextUtils.TruncateAt.END);
-                view.getTextLine2().setMaxLines(numberLines);
-            }
+            updateSuggestionViewTextMaxLines(
+                    view.getTextLine2(), model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES));
         } else if (SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR.equals(propertyKey)) {
             view.getTextLine2().setTextColor(
                     model.get(SuggestionViewProperties.TEXT_LINE_2_TEXT_COLOR));
@@ -160,13 +164,31 @@
         }
     }
 
+    /**
+     * Adjust properties of the text field to properly display single- and multi-line answers.
+     */
+    private static void updateSuggestionViewTextMaxLines(TextView view, int lines) {
+        if (lines == 1) {
+            view.setEllipsize(null);
+            view.setSingleLine();
+        } else {
+            view.setSingleLine(false);
+            view.setEllipsize(TextUtils.TruncateAt.END);
+            view.setMaxLines(lines);
+        }
+    }
+
     private static void updateSuggestionLayoutType(SuggestionView view, PropertyModel model) {
         boolean isAnswer = model.get(SuggestionViewProperties.IS_ANSWER);
-        int numberLines = model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES);
+        // Note: only one of these fields will report line count > 0; this depends on the selected
+        // suggestion layout. Old layout allows multiline response in line 2, new answer layout - in
+        // line 1.
+        boolean isMultiline = model.get(SuggestionViewProperties.TEXT_LINE_2_MAX_LINES) > 1
+                || model.get(SuggestionViewProperties.TEXT_LINE_1_MAX_LINES) > 1;
         if (!isAnswer) {
             view.setSuggestionLayoutType(SuggestionView.SuggestionLayoutType.TEXT_SUGGESTION);
         } else {
-            view.setSuggestionLayoutType(numberLines > 1
+            view.setSuggestionLayoutType(isMultiline
                             ? SuggestionView.SuggestionLayoutType.MULTI_LINE_ANSWER
                             : SuggestionView.SuggestionLayoutType.ANSWER);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
similarity index 66%
rename from chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionCoordinator.java
rename to chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
index 80066a00..c8f7441 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.omnibox.suggestions.editurl;
 
+import android.content.Context;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -14,7 +15,9 @@
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator.SuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
+import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
 import org.chromium.chrome.browser.share.ShareMenuActionHandler;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.ui.base.Clipboard;
@@ -25,7 +28,7 @@
  * suggestions list. This class also serves as a mediator, containing logic that interacts with
  * the rest of Chrome.
  */
-public class EditUrlSuggestionCoordinator implements OnClickListener {
+public class EditUrlSuggestionProcessor implements OnClickListener, SuggestionProcessor {
     /** An interface for handling taps on the suggestion rather than its buttons. */
     public interface SuggestionSelectionHandler {
         /**
@@ -53,18 +56,12 @@
     /** The name of the experiment variation that shows the copy icon. */
     private static final String COPY_ICON_VARIATION_NAME = "copy_icon";
 
-    /** The suggestion's model. */
-    private final PropertyModel mModel;
-
     /** The delegate for accessing the location bar for observation and modification. */
     private final LocationBarDelegate mLocationBarDelegate;
 
     /** A means of accessing the activity's tab. */
     private ActivityTabProvider mTabProvider;
 
-    /** The suggestion view. */
-    private ViewGroup mView;
-
     /** Whether the omnibox has already cleared its content for the focus event. */
     private boolean mHasClearedOmniboxForFocus;
 
@@ -74,63 +71,94 @@
     /** A handler for suggestion selection. */
     private SuggestionSelectionHandler mSelectionHandler;
 
+    /** The original URL that was in the omnibox when it was focused. */
+    private String mOriginalUrl;
+
+    /** The original title of the page. */
+    private String mOriginalTitle;
+
     /**
-     * @param tabProvider A means of accessing the active tab.
      * @param locationBarDelegate A means of modifying the location bar.
      * @param selectionHandler A mechanism for handling selection of the edit URL suggestion item.
      */
-    public EditUrlSuggestionCoordinator(ActivityTabProvider tabProvider,
-            LocationBarDelegate locationBarDelegate,
-            SuggestionSelectionHandler selectionHandler) {
-        mTabProvider = tabProvider;
+    public EditUrlSuggestionProcessor(
+            LocationBarDelegate locationBarDelegate, SuggestionSelectionHandler selectionHandler) {
         mLocationBarDelegate = locationBarDelegate;
-        mModel = new PropertyModel(EditUrlSuggestionProperties.ALL_KEYS);
         mSelectionHandler = selectionHandler;
     }
 
     /**
-     * Handle a specific omnibox suggestion type and determine whether a custom suggestion item
-     * should be shown.
-     * @param suggestion The omnibox suggestion being processed.
-     * @return Whether the suggestion item should be shown.
+     * Create the view specific to the suggestion this processor is responsible for.
+     * @param context An Android context.
+     * @return An edit-URL suggestion view.
      */
-    public boolean maybeReplaceOmniboxSuggestion(OmniboxSuggestion suggestion) {
+    public static ViewGroup createView(Context context) {
+        LayoutInflater inflater =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        return (ViewGroup) inflater.inflate(R.layout.edit_url_suggestion_layout, null);
+    }
+
+    @Override
+    public boolean doesProcessSuggestion(OmniboxSuggestion suggestion) {
         Tab activeTab = mTabProvider != null ? mTabProvider.getActivityTab() : null;
         if (OmniboxSuggestionType.URL_WHAT_YOU_TYPED != suggestion.getType() || activeTab == null
                 || activeTab.isIncognito()) {
             return false;
         }
         mLastProcessedSuggestion = suggestion;
+
+        // Only use the URL provided by the "what you typed" suggestion on first omnibox focus.
+        // Subsequent suggestions will provide partial URLs which we do not want. If the suggestion
+        // URL matches the original, show the suggestion item.
+        if (mOriginalUrl == null) mOriginalUrl = mLastProcessedSuggestion.getUrl();
+
+        if (!TextUtils.equals(mLastProcessedSuggestion.getUrl(), mOriginalUrl)) return false;
+
         if (!mHasClearedOmniboxForFocus) {
             mHasClearedOmniboxForFocus = true;
             mLocationBarDelegate.setOmniboxEditingText("");
         }
-        updateUrlDisplayInfo();
         return true;
     }
 
+    @Override
+    public int getViewTypeId() {
+        return OmniboxSuggestionUiType.EDIT_URL_SUGGESTION;
+    }
+
+    @Override
+    public PropertyModel createModelForSuggestion(OmniboxSuggestion suggestion) {
+        return new PropertyModel(EditUrlSuggestionProperties.ALL_KEYS);
+    }
+
+    @Override
+    public void populateModel(OmniboxSuggestion suggestion, PropertyModel model, int position) {
+        model.set(EditUrlSuggestionProperties.TEXT_CLICK_LISTENER, this);
+
+        // Check which variation of the experiment is being run.
+        String variation = getSearchReadyOmniboxVariation();
+        if (TextUtils.equals(COPY_ICON_VARIATION_NAME, variation)) {
+            model.set(EditUrlSuggestionProperties.COPY_ICON_VISIBLE, true);
+            model.set(EditUrlSuggestionProperties.SHARE_ICON_VISIBLE, false);
+        } else {
+            model.set(EditUrlSuggestionProperties.COPY_ICON_VISIBLE, false);
+            model.set(EditUrlSuggestionProperties.SHARE_ICON_VISIBLE, true);
+        }
+        model.set(EditUrlSuggestionProperties.BUTTON_CLICK_LISTENER, this);
+
+        if (mOriginalTitle == null) mOriginalTitle = mTabProvider.getActivityTab().getTitle();
+        model.set(EditUrlSuggestionProperties.TITLE_TEXT, mOriginalTitle);
+        model.set(EditUrlSuggestionProperties.URL_TEXT, mLastProcessedSuggestion.getUrl());
+    }
+
     /**
-     * @param provider A means of getting the activity's tab.
+     * @param provider A means of accessing the activity's tab.
      */
     public void setActivityTabProvider(ActivityTabProvider provider) {
         mTabProvider = provider;
     }
 
     /**
-     * @return The view to insert into the list.
-     */
-    public ViewGroup getView() {
-        return mView;
-    }
-
-    /**
-     * @return The model for the view.
-     */
-    public PropertyModel getModel() {
-        return mModel;
-    }
-
-    /**
      * Clean up any state that this coordinator has.
      */
     public void destroy() {
@@ -146,49 +174,15 @@
                 ChromeFeatureList.SEARCH_READY_OMNIBOX, FIELD_TRIAL_PARAM_NAME);
     }
 
-    /**
-     * A notification that the omnibox focus state has changed.
-     * @param hasFocus Whether the omnibox has focus.
-     */
+    @Override
     public void onUrlFocusChange(boolean hasFocus) {
         if (!hasFocus) {
+            mOriginalUrl = null;
+            mOriginalTitle = null;
             mHasClearedOmniboxForFocus = false;
             mLastProcessedSuggestion = null;
-            mView = null;
             return;
         }
-
-        // When the omnibox is focused, create a new version of the suggestion item.
-        LayoutInflater inflater = mTabProvider.getActivityTab().getActivity().getLayoutInflater();
-        mView = (ViewGroup) inflater.inflate(R.layout.edit_url_suggestion_layout, null);
-        mModel.set(EditUrlSuggestionProperties.TEXT_CLICK_LISTENER, this);
-
-        // Check which variation of the experiment is being run.
-        String variation = getSearchReadyOmniboxVariation();
-        if (COPY_ICON_VARIATION_NAME.equals(variation)) {
-            mModel.set(EditUrlSuggestionProperties.COPY_ICON_VISIBLE, true);
-            mModel.set(EditUrlSuggestionProperties.SHARE_ICON_VISIBLE, false);
-        } else {
-            mModel.set(EditUrlSuggestionProperties.COPY_ICON_VISIBLE, false);
-            mModel.set(EditUrlSuggestionProperties.SHARE_ICON_VISIBLE, true);
-        }
-        mModel.set(EditUrlSuggestionProperties.BUTTON_CLICK_LISTENER, this);
-    }
-
-    /**
-     * Update the URL info displayed in this view.
-     */
-    private void updateUrlDisplayInfo() {
-        Tab tab = mTabProvider.getActivityTab();
-        if (tab == null || mLastProcessedSuggestion == null) return;
-
-        // Only update the title if the displayed URL matches the tab's URL.
-        if (TextUtils.equals(tab.getUrl(), mLastProcessedSuggestion.getUrl())) {
-            mModel.set(EditUrlSuggestionProperties.TITLE_TEXT, tab.getTitle());
-        } else {
-            mModel.set(EditUrlSuggestionProperties.TITLE_TEXT, mLastProcessedSuggestion.getUrl());
-        }
-        mModel.set(EditUrlSuggestionProperties.URL_TEXT, mLastProcessedSuggestion.getUrl());
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProperties.java
index 89e9ecd..337d3952 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProperties.java
@@ -6,7 +6,9 @@
 
 import android.view.View;
 
+import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
 import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
 import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
 
@@ -36,6 +38,9 @@
     public static final WritableObjectPropertyKey<View.OnClickListener> TEXT_CLICK_LISTENER =
             new WritableObjectPropertyKey<>();
 
-    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {COPY_ICON_VISIBLE,
+    private static final PropertyKey[] ALL_UNIQUE_KEYS = new PropertyKey[] {COPY_ICON_VISIBLE,
             SHARE_ICON_VISIBLE, TITLE_TEXT, URL_TEXT, BUTTON_CLICK_LISTENER, TEXT_CLICK_LISTENER};
+
+    public static final PropertyKey[] ALL_KEYS =
+            PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java
index 6ef542a..08c56df5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java
@@ -43,8 +43,7 @@
                             model.get(EditUrlSuggestionProperties.BUTTON_CLICK_LISTENER));
         } else if (EditUrlSuggestionProperties.TEXT_CLICK_LISTENER == propertyKey) {
             view.setOnClickListener(model.get(EditUrlSuggestionProperties.TEXT_CLICK_LISTENER));
-        } else {
-            throw new RuntimeException("Unknown property key in EditUrlSuggestionViewBinder!");
         }
+        // TODO(mdjones): Support SuggestionCommonProperties.*
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 61af217..04b61308 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -189,6 +189,13 @@
     public static final String NIGHT_MODE_AVAILABLE_KEY = "night_mode_available";
 
     /**
+     * Whether or not the download auto-resumption is enabled in native.
+     * Default value is true.
+     */
+    public static final String DOWNLOAD_AUTO_RESUMPTION_IN_NATIVE_KEY =
+            "download_auto_resumption_in_native";
+
+    /**
      * Marks that the content suggestions surface has been shown.
      * Default value is false.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index fae0e23..c43a964 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -146,7 +146,7 @@
                 R.id.search_location_bar);
         mSearchBox.setDelegate(this);
         mSearchBox.setToolbarDataProvider(mSearchBoxDataProvider);
-        mSearchBox.initializeControls(new WindowDelegate(getWindow()), getWindowAndroid());
+        mSearchBox.initializeControls(new WindowDelegate(getWindow()), getWindowAndroid(), null);
 
         // Kick off everything needed for the user to type into the box.
         beginQuery();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
index 70eed20..a2a3c82 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java
@@ -89,11 +89,6 @@
     }
 
     @Override
-    public boolean shouldShowVerboseStatus() {
-        return false;
-    }
-
-    @Override
     public int getSecurityLevel() {
         return ConnectionSecurityLevel.NONE;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index 935b5922..434abc7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -320,19 +320,6 @@
     }
 
     @Override
-    public boolean shouldShowVerboseStatus() {
-        int securityLevel = getSecurityLevel();
-        if (isPreview() && securityLevel != ConnectionSecurityLevel.DANGEROUS) {
-            return true;
-        }
-        // Because is offline page is cleared a bit slower, we also ensure that connection security
-        // level is NONE or HTTP_SHOW_WARNING (http://crbug.com/671453).
-        return isOfflinePage()
-                && (securityLevel == ConnectionSecurityLevel.NONE
-                           || securityLevel == ConnectionSecurityLevel.HTTP_SHOW_WARNING);
-    }
-
-    @Override
     public int getSecurityLevel() {
         Tab tab = getTab();
         return getSecurityLevel(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
index c52d03a..76abe627 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarDataProvider.java
@@ -85,11 +85,6 @@
     boolean isPreview();
 
     /**
-     * @return Whether verbose status next to the security icon should be displayed.
-     */
-    boolean shouldShowVerboseStatus();
-
-    /**
      * @return The current {@link ConnectionSecurityLevel}.
      */
     @ConnectionSecurityLevel
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 6196797..a1c6d3bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -342,8 +342,8 @@
         mLocationBar.addUrlFocusChangeListener(this);
         mLocationBar.setDefaultTextEditActionModeCallback(
                 mActionModeController.getActionModeCallback());
-        mLocationBar.initializeControls(
-                new WindowDelegate(mActivity.getWindow()), mActivity.getWindowAndroid());
+        mLocationBar.initializeControls(new WindowDelegate(mActivity.getWindow()),
+                mActivity.getWindowAndroid(), mActivity.getActivityTabProvider());
         mLocationBar.addUrlFocusChangeListener(mLocationBarFocusObserver);
 
         setMenuHandler(menuHandler);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index ebf63b9..70e96e7a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -43,6 +43,7 @@
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.WindowDelegate;
 import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper;
 import org.chromium.chrome.browser.native_page.NativePageFactory;
@@ -532,7 +533,8 @@
     }
 
     @Override
-    public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid) {}
+    public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid,
+            ActivityTabProvider provider) {}
 
     @Override
     public void updateStatusIcon() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 78d05e1..fc38291 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -237,11 +237,6 @@
             }
 
             @Override
-            public boolean shouldShowVerboseStatus() {
-                return false;
-            }
-
-            @Override
             public int getSecurityLevel() {
                 return ConnectionSecurityLevel.NONE;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java
index 013c1c0..bd2b5a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/PageViewObserver.java
@@ -4,10 +4,14 @@
 
 package org.chromium.chrome.browser.usage_stats;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
 import android.net.Uri;
 import android.webkit.URLUtil;
 
+import org.chromium.base.Log;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.Tab.TabHidingType;
@@ -16,25 +20,33 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabSelectionType;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 /**
  * Class that observes url and tab changes in order to track when browsing stops and starts for each
  * visited fully-qualified domain name (FQDN).
  */
+@SuppressLint("NewApi")
 public class PageViewObserver {
+    private static final String TAG = "PageViewObserver";
+
     private final Activity mActivity;
     private final TabModelSelectorTabModelObserver mTabModelObserver;
     private final TabModelSelector mTabModelSelector;
     private final TabObserver mTabObserver;
     private final EventTracker mEventTracker;
+    private final TokenTracker mTokenTracker;
 
     private Tab mCurrentTab;
     private String mLastFqdn;
 
-    public PageViewObserver(
-            Activity activity, TabModelSelector tabModelSelector, EventTracker eventTracker) {
+    public PageViewObserver(Activity activity, TabModelSelector tabModelSelector,
+            EventTracker eventTracker, TokenTracker tokenTracker) {
         mActivity = activity;
         mTabModelSelector = tabModelSelector;
         mEventTracker = eventTracker;
+        mTokenTracker = tokenTracker;
         mTabObserver = new EmptyTabObserver() {
             @Override
             public void onShown(Tab tab, @TabSelectionType int type) {
@@ -93,6 +105,7 @@
         if (mLastFqdn != null) {
             mEventTracker.addWebsiteEvent(new WebsiteEvent(
                     System.currentTimeMillis(), mLastFqdn, WebsiteEvent.EventType.STOP));
+            reportToPlatformIfDomainIsTracked("reportUsageStop", mLastFqdn);
             mLastFqdn = null;
         }
 
@@ -101,6 +114,7 @@
         mLastFqdn = newFqdn;
         mEventTracker.addWebsiteEvent(new WebsiteEvent(
                 System.currentTimeMillis(), mLastFqdn, WebsiteEvent.EventType.START));
+        reportToPlatformIfDomainIsTracked("reportUsageStart", mLastFqdn);
     }
 
     private void switchObserverToTab(Tab tab) {
@@ -113,4 +127,20 @@
             mCurrentTab.addObserver(mTabObserver);
         }
     }
+
+    private void reportToPlatformIfDomainIsTracked(String reportMethodName, String fqdn) {
+        String token = mTokenTracker.getTokenForFqdn(fqdn);
+        if (token == null) return;
+
+        try {
+            UsageStatsManager instance =
+                    (UsageStatsManager) mActivity.getSystemService(Context.USAGE_STATS_SERVICE);
+            Method reportMethod = UsageStatsManager.class.getDeclaredMethod(
+                    reportMethodName, Activity.class, String.class);
+
+            reportMethod.invoke(instance, mActivity, token);
+        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
+            Log.e(TAG, "Failed to report to platform API", e);
+        }
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
index f97eb1f..aafc000f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsService.java
@@ -56,7 +56,7 @@
     public PageViewObserver createPageViewObserver(
             TabModelSelector tabModelSelector, Activity activity) {
         ThreadUtils.assertOnUiThread();
-        return new PageViewObserver(activity, tabModelSelector, mEventTracker);
+        return new PageViewObserver(activity, tabModelSelector, mEventTracker, mTokenTracker);
     }
 
     /** @return Whether the user has authorized DW to access usage stats data. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 4a256dd..46829e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -21,6 +21,7 @@
 import org.chromium.base.SysUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -77,6 +78,8 @@
     private static Boolean sShouldInflateToolbarOnBackgroundThread;
     private static Boolean sIsNightModeAvailable;
 
+    private static Boolean sDownloadAutoResumptionEnabledInNative;
+
     private static final String NTP_BUTTON_TRIAL_NAME = "NewTabPage";
     private static final String NTP_BUTTON_VARIANT_PARAM_NAME = "variation";
 
@@ -189,6 +192,7 @@
         cacheBottomToolbarEnabled();
         cacheInflateToolbarOnBackgroundThread();
         cacheNightModeAvailable();
+        cacheDownloadAutoResumptionEnabledInNative();
 
         // Propagate DONT_PREFETCH_LIBRARIES feature value to LibraryLoader. This can't
         // be done in LibraryLoader itself because it lives in //base and can't depend
@@ -267,6 +271,22 @@
     }
 
     /**
+     * @return Whether or not the download auto-resumptions should be enabled in native.
+     */
+    @CalledByNative
+    public static boolean isDownloadAutoResumptionEnabledInNative() {
+        if (sDownloadAutoResumptionEnabledInNative == null) {
+            ChromePreferenceManager prefManager = ChromePreferenceManager.getInstance();
+
+            try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+                sDownloadAutoResumptionEnabledInNative = prefManager.readBoolean(
+                        ChromePreferenceManager.DOWNLOAD_AUTO_RESUMPTION_IN_NATIVE_KEY, true);
+            }
+        }
+        return sDownloadAutoResumptionEnabledInNative;
+    }
+
+    /**
      * Cache whether or not the new tab page button is enabled so on next startup, the value can
      * be made available immediately.
      */
@@ -337,6 +357,16 @@
     }
 
     /**
+     * Cache whether or not download auto-resumptions are enabled in native so on next startup, the
+     * value can be made available immediately.
+     */
+    private static void cacheDownloadAutoResumptionEnabledInNative() {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.DOWNLOAD_AUTO_RESUMPTION_IN_NATIVE_KEY,
+                ChromeFeatureList.isEnabled(ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE));
+    }
+
+    /**
      * @return Whether or not the bottom toolbar is enabled.
      */
     public static boolean isBottomToolbarEnabled() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java
index 1246753e6..04e1d957 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappSplashScreenController.java
@@ -148,27 +148,27 @@
     @Override
     public void didFirstVisuallyNonEmptyPaint(Tab tab) {
         if (canHideSplashScreen()) {
-            hideSplashScreenOnDrawingFinished(tab, SplashHidesReason.PAINT);
+            hideSplash(tab, SplashHidesReason.PAINT);
         }
     }
 
     @Override
     public void onPageLoadFinished(Tab tab, String url) {
         if (canHideSplashScreen()) {
-            hideSplashScreenOnDrawingFinished(tab, SplashHidesReason.LOAD_FINISHED);
+            hideSplash(tab, SplashHidesReason.LOAD_FINISHED);
         }
     }
 
     @Override
     public void onPageLoadFailed(Tab tab, int errorCode) {
         if (canHideSplashScreen()) {
-            animateHidingSplashScreen(tab, SplashHidesReason.LOAD_FAILED);
+            hideSplash(tab, SplashHidesReason.LOAD_FAILED);
         }
     }
 
     @Override
     public void onCrash(Tab tab) {
-        animateHidingSplashScreen(tab, SplashHidesReason.CRASH);
+        hideSplash(tab, SplashHidesReason.CRASH);
     }
 
     @Override
@@ -288,21 +288,19 @@
         }
     }
 
-    /**
-     * Schedules the splash screen hiding once the compositor has finished drawing a frame.
-     *
-     * Without this callback we were seeing a short flash of white between the splash screen and
-     * the web content (crbug.com/734500).
-     * */
-    private void hideSplashScreenOnDrawingFinished(
-            final Tab tab, final @SplashHidesReason int reason) {
+    /** Schedules the splash screen hiding once the compositor has finished drawing a frame. */
+    private void hideSplash(final Tab tab, final @SplashHidesReason int reason) {
+        // Don't try to hide the splash screen again if is already hidden.
         if (mSplashScreen == null) return;
 
-        if (mCompositorViewHolder == null) {
+        if (reason == SplashHidesReason.LOAD_FAILED || reason == SplashHidesReason.CRASH) {
             animateHidingSplashScreen(tab, reason);
             return;
         }
 
+        // Delay hiding the splash screen till the compositor has finished drawing the next frame.
+        // Without this callback we were seeing a short flash of white between the splash screen and
+        // the web content (crbug.com/734500).
         mCompositorViewHolder.getCompositorView().surfaceRedrawNeededAsync(
                 () -> { animateHidingSplashScreen(tab, reason); });
     }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 2cf420e..4ca7824 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1173,7 +1173,7 @@
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionView.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/basic/SuggestionViewViewBinder.java",
-  "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionCoordinator.java",
+  "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProperties.java",
   "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java",
   "java/src/org/chromium/chrome/browser/page_info/CertificateChainHelper.java",
@@ -1987,7 +1987,6 @@
   "javatests/src/org/chromium/chrome/browser/dom_distiller/DistillabilityServiceTest.java",
   "javatests/src/org/chromium/chrome/browser/dom_distiller/DistilledPagePrefsTest.java",
   "javatests/src/org/chromium/chrome/browser/download/ChromeDownloadDelegateTest.java",
-  "javatests/src/org/chromium/chrome/browser/download/DisableAnimationsRule.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java",
   "javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceTest.java",
@@ -2360,7 +2359,9 @@
   "javatests/src/org/chromium/chrome/browser/widget/bottomsheet/TestBottomSheetContent.java",
   "javatests/src/org/chromium/chrome/browser/widget/findinpage/FindTest.java",
   "javatests/src/org/chromium/chrome/test/crash/IntentionalCrashTest.java",
+  "javatests/src/org/chromium/chrome/test/ui/DisableAnimationsTestRule.java",
   "javatests/src/org/chromium/chrome/test/ui/DummyUiActivity.java",
+  "javatests/src/org/chromium/chrome/test/ui/DummyUiActivityTestCase.java",
   "javatests/src/org/chromium/chrome/test/util/ChromeSigninUtilsTest.java",
 ]
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
index ba79d0fd..14b13403 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java
@@ -31,6 +31,7 @@
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -88,10 +89,10 @@
         mHttpsOrigin = new Origin("https://www.example.com");
         mHttpOrigin = new Origin("http://www.android.com");
 
-        mHandleAllUrlsVerifier =
-                new OriginVerifier(PACKAGE_NAME, CustomTabsService.RELATION_HANDLE_ALL_URLS);
-        mUseAsOriginVerifier =
-                new OriginVerifier(PACKAGE_NAME, CustomTabsService.RELATION_USE_AS_ORIGIN);
+        mHandleAllUrlsVerifier = new OriginVerifier(new TestOriginVerificationListener(),
+                PACKAGE_NAME, CustomTabsService.RELATION_HANDLE_ALL_URLS);
+        mUseAsOriginVerifier = new OriginVerifier(new TestOriginVerificationListener(),
+                PACKAGE_NAME, CustomTabsService.RELATION_USE_AS_ORIGIN);
         mVerificationResultSemaphore = new Semaphore(0);
     }
 
@@ -106,14 +107,12 @@
     @Test
     @SmallTest
     public void testOnlyHttpsAllowed() throws InterruptedException {
-        Origin origin = new Origin(Uri.parse("LOL"));
-        ThreadUtils.postOnUiThread(() ->
-                mHandleAllUrlsVerifier.start(new TestOriginVerificationListener(), origin));
+        ThreadUtils.postOnUiThread(()
+                -> mHandleAllUrlsVerifier.start(new Origin(Uri.parse("LOL"))));
         Assert.assertTrue(
                 mVerificationResultSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         Assert.assertFalse(mLastVerified);
-        ThreadUtils.postOnUiThread(() ->
-                mHandleAllUrlsVerifier.start(new TestOriginVerificationListener(), mHttpOrigin));
+        ThreadUtils.postOnUiThread(() -> mHandleAllUrlsVerifier.start(mHttpOrigin));
         Assert.assertTrue(
                 mVerificationResultSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         Assert.assertFalse(mLastVerified);
@@ -122,20 +121,28 @@
     @Test
     @SmallTest
     public void testMultipleRelationships() throws Exception {
-        ThreadUtils.postOnUiThread(() ->
-                OriginVerifier.addVerificationOverride(
-                        PACKAGE_NAME, mHttpsOrigin, CustomTabsService.RELATION_USE_AS_ORIGIN));
-        ThreadUtils.postOnUiThread(() ->
-                mUseAsOriginVerifier.start(new TestOriginVerificationListener(), mHttpsOrigin));
+        ThreadUtils.postOnUiThread(
+                ()
+                        -> OriginVerifier.addVerifiedOriginForPackage(PACKAGE_NAME,
+                        mHttpsOrigin, CustomTabsService.RELATION_USE_AS_ORIGIN));
+        ThreadUtils.postOnUiThread(() -> mUseAsOriginVerifier.start(mHttpsOrigin));
         Assert.assertTrue(
                 mVerificationResultSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         Assert.assertTrue(mLastVerified);
-        Assert.assertTrue(ThreadUtils.runOnUiThreadBlocking(
-                () -> OriginVerifier.wasPreviouslyVerified(PACKAGE_NAME, mHttpsOrigin,
-                        CustomTabsService.RELATION_USE_AS_ORIGIN)));
-        Assert.assertFalse(ThreadUtils.runOnUiThreadBlocking(
-                () -> OriginVerifier.wasPreviouslyVerified(PACKAGE_NAME, mHttpsOrigin,
-                        CustomTabsService.RELATION_HANDLE_ALL_URLS)));
+        Assert.assertTrue(ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return OriginVerifier.isValidOrigin(PACKAGE_NAME, mHttpsOrigin,
+                        CustomTabsService.RELATION_USE_AS_ORIGIN);
+            }
+        }));
+        Assert.assertFalse(ThreadUtils.runOnUiThreadBlocking(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return OriginVerifier.isValidOrigin(PACKAGE_NAME, mHttpsOrigin,
+                        CustomTabsService.RELATION_HANDLE_ALL_URLS);
+            }
+        }));
         Assert.assertEquals(mLastPackageName, PACKAGE_NAME);
         Assert.assertEquals(mLastOrigin, mHttpsOrigin);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
index dd0295d0..e84dd1f6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityTest.java
@@ -77,7 +77,7 @@
     /** Caches a successful verification for the given |packageName| and |url|. */
     private static void spoofVerification(String packageName, String url) {
         ThreadUtils.runOnUiThreadBlocking(
-                () -> OriginVerifier.addVerificationOverride(packageName, new Origin(url),
+                () -> OriginVerifier.addVerifiedOriginForPackage(packageName, new Origin(url),
                         CustomTabsService.RELATION_HANDLE_ALL_URLS));
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
index 671c048a..573c3d9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/ClientManagerTest.java
@@ -192,7 +192,7 @@
                 Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
 
                 // If there is a prepopulated origin, we should get a synchronous verification.
-                OriginVerifier.addVerificationOverride(
+                OriginVerifier.addVerifiedOriginForPackage(
                         ContextUtils.getApplicationContext().getPackageName(), new Origin(URL),
                         CustomTabsService.RELATION_USE_AS_ORIGIN);
                 cm.verifyAndInitializeWithPostMessageOriginForSession(
@@ -240,7 +240,7 @@
         Assert.assertNull(cm.getPostMessageOriginForSessionForTesting(mSession));
         ThreadUtils.runOnUiThreadBlocking(() -> {
             // Prepopulated origins should depend on the relation used.
-            OriginVerifier.addVerificationOverride(
+            OriginVerifier.addVerifiedOriginForPackage(
                     ContextUtils.getApplicationContext().getPackageName(), origin,
                     CustomTabsService.RELATION_HANDLE_ALL_URLS);
             // This uses CustomTabsService.RELATION_USE_AS_ORIGIN by default.
@@ -282,7 +282,7 @@
 
                 // Even if there is a prepopulated origin, non-https origins should get an early
                 // return with false.
-                OriginVerifier.addVerificationOverride(
+                OriginVerifier.addVerifiedOriginForPackage(
                         ContextUtils.getApplicationContext().getPackageName(), origin,
                         CustomTabsService.RELATION_USE_AS_ORIGIN);
                 cm.verifyAndInitializeWithPostMessageOriginForSession(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 6e17a46..0e68492a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -1255,7 +1255,7 @@
         connection.newSession(token);
         connection.overridePackageNameForSessionForTesting(token, "app1");
         ThreadUtils.runOnUiThreadBlocking(
-                () -> OriginVerifier.addVerificationOverride("app1", new Origin(referrer),
+                () -> OriginVerifier.addVerifiedOriginForPackage("app1", new Origin(referrer),
                         CustomTabsService.RELATION_USE_AS_ORIGIN));
 
         final CustomTabsSessionToken session = warmUpAndLaunchUrlWithSession(intent);
@@ -1944,7 +1944,7 @@
         Assert.assertTrue(connection.newSession(token));
         connection.mClientManager.setAllowParallelRequestForSession(token, true);
         ThreadUtils.runOnUiThreadBlocking(() -> {
-            OriginVerifier.addVerificationOverride(
+            OriginVerifier.addVerifiedOriginForPackage(
                     context.getPackageName(), origin, CustomTabsService.RELATION_USE_AS_ORIGIN);
         });
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index f722964..b47c10b44 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -93,7 +93,7 @@
                 () -> Assert.assertFalse(mConnection.canDoParallelRequest(session, ORIGIN)));
         ThreadUtils.runOnUiThreadBlocking(() -> {
             String packageName = mContext.getPackageName();
-            OriginVerifier.addVerificationOverride(packageName, new Origin(ORIGIN.toString()),
+            OriginVerifier.addVerifiedOriginForPackage(packageName, new Origin(ORIGIN.toString()),
                     CustomTabsService.RELATION_USE_AS_ORIGIN);
             Assert.assertTrue(mConnection.canDoParallelRequest(session, ORIGIN));
         });
@@ -107,7 +107,7 @@
         Assert.assertTrue(mConnection.newSession(session));
         ThreadUtils.runOnUiThreadBlocking(() -> {
             String packageName = mContext.getPackageName();
-            OriginVerifier.addVerificationOverride(packageName, new Origin(ORIGIN.toString()),
+            OriginVerifier.addVerifiedOriginForPackage(packageName, new Origin(ORIGIN.toString()),
 
                     CustomTabsService.RELATION_USE_AS_ORIGIN);
         });
@@ -523,7 +523,7 @@
         mConnection.mClientManager.setAllowParallelRequestForSession(token, true);
         mConnection.mClientManager.setAllowResourcePrefetchForSession(token, true);
         ThreadUtils.runOnUiThreadBlocking(() -> {
-            OriginVerifier.addVerificationOverride(mContext.getPackageName(),
+            OriginVerifier.addVerifiedOriginForPackage(mContext.getPackageName(),
                     new Origin(origin.toString()), CustomTabsService.RELATION_USE_AS_ORIGIN);
             Assert.assertTrue(mConnection.canDoParallelRequest(token, origin));
         });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
index b2911bbd..fb4f910 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadActivityTest.java
@@ -62,6 +62,7 @@
 import org.chromium.chrome.browser.widget.ListMenuButton.Item;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.offline_items_collection.OfflineItem;
@@ -80,7 +81,7 @@
 public class DownloadActivityTest {
     // Disable animations to reduce flakiness.
     @ClassRule
-    public static DisableAnimationsRule disableAnimationsRule = new DisableAnimationsRule();
+    public static DisableAnimationsTestRule disableAnimationsRule = new DisableAnimationsTestRule();
 
     @Rule
     public ActivityTestRule<DownloadActivity> mActivityTestRule =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
index d18a8d85..925f31a9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -28,6 +28,7 @@
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.browser.download.DownloadInfo.Builder;
 import org.chromium.chrome.browser.download.DownloadManagerServiceTest.MockDownloadNotifier.MethodID;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.OfflineItem.Progress;
@@ -485,6 +486,9 @@
     @Feature({"Download"})
     @RetryOnFailure
     public void testInterruptedDownloadAreAutoResumed() throws InterruptedException {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.DOWNLOAD_AUTO_RESUMPTION_IN_NATIVE_KEY, false);
+
         MockDownloadNotifier notifier = new MockDownloadNotifier();
         createDownloadManagerService(notifier, UPDATE_DELAY_FOR_TEST);
         DownloadManagerService.disableNetworkListenerForTest();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
index 29c9d505..3e342e9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/home/DownloadActivityV2Test.java
@@ -10,17 +10,12 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 
 import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
 
-import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
@@ -28,7 +23,6 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.download.DisableAnimationsRule;
 import org.chromium.chrome.browser.download.home.list.UiUtils;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
 import org.chromium.chrome.browser.download.ui.StubbedProvider;
@@ -37,6 +31,7 @@
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ui.DummyUiActivity;
+import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.ui.test.util.UiRestriction;
@@ -44,21 +39,10 @@
 import java.util.HashMap;
 import java.util.Map;
 
-/** Tests the DownloadActivity and the DownloadManagerUi. */
+/** Tests the DownloadActivity home V2. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-public class DownloadActivityV2Test {
-    // Disable animations to reduce flakiness.
-    @ClassRule
-    public static DisableAnimationsRule disableAnimationsRule = new DisableAnimationsRule();
-
-    @Rule
-    public ActivityTestRule<DummyUiActivity> mActivityTestRule =
-            new ActivityTestRule<>(DummyUiActivity.class);
-
-    @Rule
-    public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
+public class DownloadActivityV2Test extends DummyUiActivityTestCase {
     @Mock
     private Profile mProfile;
     @Mock
@@ -66,7 +50,6 @@
     @Mock
     private SnackbarManager mSnackbarManager;
 
-    private DummyUiActivity mActivity;
     private DownloadManagerCoordinator mDownloadCoordinator;
 
     @BeforeClass
@@ -77,8 +60,10 @@
         DummyUiActivity.setTestTheme(org.chromium.chrome.R.style.FullscreenWhiteActivityTheme);
     }
 
-    @Before
-    public void setUp() throws Exception {
+    @Override
+    public void setUpTest() throws Exception {
+        super.setUpTest();
+        MockitoAnnotations.initMocks(this);
         // TODO(yliuyliu): Write a new StubbedOfflineContentProvider for new Download UI testing.
         StubbedProvider stubbedProvider = new StubbedProvider();
         OfflineContentAggregatorFactory.setOfflineContentProviderForTests(
@@ -102,8 +87,6 @@
         features.put(ChromeFeatureList.DOWNLOAD_HOME_V2, true);
         features.put(ChromeFeatureList.OFFLINE_PAGES_PREFETCHING, true);
         ChromeFeatureList.setTestFeatures(features);
-
-        mActivity = mActivityTestRule.getActivity();
     }
 
     private void setUpUi() {
@@ -113,9 +96,9 @@
                                                  .setUseNewDownloadPath(true)
                                                  .setUseNewDownloadPathThumbnails(true)
                                                  .build();
-        mDownloadCoordinator =
-                new DownloadManagerCoordinatorImpl(mProfile, mActivity, config, mSnackbarManager);
-        mActivity.setContentView(mDownloadCoordinator.getView());
+        mDownloadCoordinator = new DownloadManagerCoordinatorImpl(
+                mProfile, getActivity(), config, mSnackbarManager);
+        getActivity().setContentView(mDownloadCoordinator.getView());
 
         mDownloadCoordinator.updateForUrl(UrlConstants.DOWNLOADS_URL);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
index 82a7128..d8ebb43 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarVoiceRecognitionHandlerTest.java
@@ -230,11 +230,6 @@
         }
 
         @Override
-        public boolean shouldShowVerboseStatus() {
-            return false;
-        }
-
-        @Override
         public int getSecurityLevel() {
             return 0;
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java
index 231d543..ad355ee 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserCompositorViewHolderTest.java
@@ -6,7 +6,6 @@
 
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_SVR;
 
-import android.os.Build;
 import android.support.test.filters.MediumTest;
 import android.view.ViewGroup;
 
@@ -17,7 +16,6 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -36,8 +34,6 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Restriction(RESTRICTION_TYPE_SVR)
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N)
 public class VrBrowserCompositorViewHolderTest {
     // We explicitly instantiate a rule here instead of using parameterization since this class
     // only ever runs in ChromeTabbedActivity.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
index 3428633..d992e1b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
@@ -16,7 +16,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.PointF;
-import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
@@ -31,7 +30,6 @@
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -63,8 +61,6 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N)
 public class VrBrowserTransitionTest {
     // We explicitly instantiate a rule here instead of using parameterization since this class
     // only ever runs in ChromeTabbedActivity.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java
index bf25500..21d1fad2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrInstallUpdateInfoBarTest.java
@@ -51,8 +51,7 @@
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebVR is only supported on K+
+@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT) // WebVR is only supported on K+
 @Restriction(RESTRICTION_TYPE_SVR)
 public class VrInstallUpdateInfoBarTest {
     @ClassParameter
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
index b95ba37..d86efb3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
@@ -46,8 +46,7 @@
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-webvr"})
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebVR is only supported on K+
+@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT) // WebVR is only supported on K+
 public class WebXrVrDeviceTest {
     @ClassParameter
     private static List<ParameterSet> sClassParams =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java
index b45b03a..3809d19 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrInputTest.java
@@ -65,8 +65,7 @@
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-webvr"})
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebVR and WebXR are only supported on K+
+@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT) // WebVR and WebXR are only supported on K+
 public class WebXrVrInputTest {
     @ClassParameter
     private static List<ParameterSet> sClassParams =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java
index 84e9245..3dca686 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTabTest.java
@@ -43,8 +43,7 @@
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-webvr"})
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebVR is only supported on K+
+@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT) // WebVR is only supported on K+
 public class WebXrVrTabTest {
     @ClassParameter
     private static List<ParameterSet> sClassParams =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
index 78f3913..04c5c59 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrTransitionTest.java
@@ -65,8 +65,7 @@
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-webvr"})
-// TODO(crbug.com/897259): consistently failing on pre-N.
-@MinAndroidSdkLevel(Build.VERSION_CODES.N) // WebVR and WebXR are only supported on K+
+@MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT) // WebVR and WebXR are only supported on K+
 @TargetApi(Build.VERSION_CODES.KITKAT) // Necessary to allow taking screenshots with UiAutomation
 public class WebXrVrTransitionTest {
     @ClassParameter
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DisableAnimationsRule.java b/chrome/android/javatests/src/org/chromium/chrome/test/ui/DisableAnimationsTestRule.java
similarity index 92%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/download/DisableAnimationsRule.java
rename to chrome/android/javatests/src/org/chromium/chrome/test/ui/DisableAnimationsTestRule.java
index 433c1b9..e6dcb30c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DisableAnimationsRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/test/ui/DisableAnimationsTestRule.java
@@ -2,7 +2,7 @@
 // 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.download;
+package org.chromium.chrome.test.ui;
 
 import android.os.IBinder;
 
@@ -18,21 +18,21 @@
 /**
  * {@link TestRule} to disable animations for UI testing.
  */
-public class DisableAnimationsRule implements TestRule {
+public class DisableAnimationsTestRule implements TestRule {
     private Method mSetAnimationScalesMethod;
     private Method mGetAnimationScalesMethod;
     private Object mWindowManagerObject;
 
     private static final float DISABLED_SCALE_FACTOR = 0.0f;
     private static final float DEFAULT_SCALE_FACTOR = 1.0f;
-    private static final String TAG = "disable_animations";
+    private static final String TAG = "DisableAnimations";
 
     /**
      * Invoke setAnimationScalesMethod to turn off system animations, such as Window animation
      * scale, Transition animation scale, Animator duration scale, which can improve stability
      * and reduce flakiness for UI testing.
      */
-    public DisableAnimationsRule() {
+    public DisableAnimationsTestRule() {
         try {
             Class<?> windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub");
             Method asInterface =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/test/ui/DummyUiActivityTestCase.java b/chrome/android/javatests/src/org/chromium/chrome/test/ui/DummyUiActivityTestCase.java
new file mode 100644
index 0000000..350a36f
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/test/ui/DummyUiActivityTestCase.java
@@ -0,0 +1,69 @@
+// 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.
+
+package org.chromium.chrome.test.ui;
+
+import android.support.test.rule.ActivityTestRule;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Test case to instrument DummyUiActivity for UI testing scenarios.
+ * Recommend to use setUpTest() and tearDownTest() to setup and tear down instead of @Before and
+ * @After.
+ */
+public class DummyUiActivityTestCase {
+    private DummyUiActivity mActivity;
+
+    private ActivityTestRule<DummyUiActivity> mActivityTestRule =
+            new ActivityTestRule<>(DummyUiActivity.class);
+
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule disableAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public TestRule ruleChain = RuleChain.outerRule(mActivityTestRule).around(new TestDriverRule());
+
+    /**
+     * TestRule to setup and tear down for each test.
+     */
+    public final class TestDriverRule implements TestRule {
+        @Override
+        public Statement apply(final Statement base, Description description) {
+            return new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    setUpTest();
+                    try {
+                        base.evaluate();
+                    } finally {
+                        tearDownTest();
+                    }
+                }
+            };
+        }
+    }
+
+    // Override this to setup before test.
+    public void setUpTest() throws Exception {
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    // Override this to tear down after test.
+    public void tearDownTest() throws Exception {}
+
+    public DummyUiActivity getActivity() {
+        return mActivity;
+    }
+
+    public ActivityTestRule<DummyUiActivity> getActivityTestRule() {
+        return mActivityTestRule;
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/DownloadResumptionSchedulerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/DownloadResumptionSchedulerTest.java
index 49997c8..0d7b924 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/DownloadResumptionSchedulerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/DownloadResumptionSchedulerTest.java
@@ -28,6 +28,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
 import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
 import org.chromium.components.background_task_scheduler.TaskIds;
@@ -54,6 +55,8 @@
 
     @Before
     public void setUp() {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                ChromePreferenceManager.DOWNLOAD_AUTO_RESUMPTION_IN_NATIVE_KEY, false);
         BackgroundTaskSchedulerFactory.setSchedulerForTesting(mScheduler);
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
index ed9ef2c..7e07750 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java
@@ -55,6 +55,8 @@
     private Tab mTab2;
     @Mock
     private EventTracker mEventTracker;
+    @Mock
+    private TokenTracker mTokenTracker;
     @Captor
     private ArgumentCaptor<TabObserver> mTabObserverCaptor;
     @Captor
@@ -178,9 +180,11 @@
         verify(mEventTracker, times(0)).addWebsiteEvent(argThat(isStopEvent(DIFFERENT_FQDN)));
     }
 
+    // TODO(pnoland): add test for platform reporting once the System API is available in Q.
+
     private PageViewObserver createPageViewObserver() {
         PageViewObserver observer =
-                new PageViewObserver(mActivity, mTabModelSelector, mEventTracker);
+                new PageViewObserver(mActivity, mTabModelSelector, mEventTracker, mTokenTracker);
         verify(mTabModel, times(1)).addObserver(mTabModelObserverCaptor.capture());
         if (mTabModelSelector.getCurrentTab() != null) {
             verify(mTabModelSelector.getCurrentTab(), times(1))
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 1329442..8d25496 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -631,10 +631,10 @@
     "invalidation/profile_invalidation_provider_factory.h",
     "io_thread.cc",
     "io_thread.h",
-    "language/chrome_language_detection_tab_helper.cc",
-    "language/chrome_language_detection_tab_helper.h",
     "language/language_model_manager_factory.cc",
     "language/language_model_manager_factory.h",
+    "language/translate_frame_binder.cc",
+    "language/translate_frame_binder.h",
     "language/url_language_histogram_factory.cc",
     "language/url_language_histogram_factory.h",
     "lifetime/application_lifetime.cc",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 1a2fc6b..0cf79111 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -15,6 +15,7 @@
 #include "chrome/common/chrome_features.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
+#include "components/download/public/common/download_features.h"
 #include "components/feed/feed_feature_list.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/language/core/common/language_experiments.h"
@@ -58,6 +59,7 @@
     &contextual_suggestions::kContextualSuggestionsButton,
     &contextual_suggestions::kContextualSuggestionsIPHReverseScroll,
     &contextual_suggestions::kContextualSuggestionsOptOut,
+    &download::features::kDownloadAutoResumptionNative,
     &features::kAllowStartingServiceManagerOnly,
     &features::kAppNotificationStatusMessaging,
     &features::kClearOldBrowsingData,
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index 734d700..f09fad7 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/android/download/download_controller.h"
 #include "chrome/browser/android/download/download_utils.h"
 #include "chrome/browser/android/download/service/download_task_scheduler.h"
+#include "chrome/browser/android/feature_utilities.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_core_service.h"
 #include "chrome/browser/download/download_core_service_factory.h"
@@ -83,6 +84,8 @@
   auto config = std::make_unique<download::AutoResumptionHandler::Config>();
   config->auto_resumption_size_limit =
       DownloadUtils::GetAutoResumptionSizeLimit();
+  config->is_auto_resumption_enabled_in_native =
+      chrome::android::IsDownloadAutoResumptionEnabledInNative();
   download::AutoResumptionHandler::Create(
       std::move(network_listener), std::move(task_manager), std::move(config));
 }
diff --git a/chrome/browser/android/feature_utilities.cc b/chrome/browser/android/feature_utilities.cc
index d07a353..6a3491c 100644
--- a/chrome/browser/android/feature_utilities.cc
+++ b/chrome/browser/android/feature_utilities.cc
@@ -30,6 +30,11 @@
   return is_in_multi_window_mode;
 }
 
+bool IsDownloadAutoResumptionEnabledInNative() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_FeatureUtilities_isDownloadAutoResumptionEnabledInNative(env);
+}
+
 } // namespace android
 } // namespace chrome
 
diff --git a/chrome/browser/android/feature_utilities.h b/chrome/browser/android/feature_utilities.h
index 287c1a1f..508ea0a 100644
--- a/chrome/browser/android/feature_utilities.h
+++ b/chrome/browser/android/feature_utilities.h
@@ -20,6 +20,8 @@
 
 bool GetIsInMultiWindowModeValue();
 
+bool IsDownloadAutoResumptionEnabledInNative();
+
 } // namespace android
 } // namespace chrome
 
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 95f22a2..14b9bb38 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -104,7 +104,6 @@
 
 class TabAndroidHelper : public content::WebContentsUserData<TabAndroidHelper> {
  public:
-  explicit TabAndroidHelper(content::WebContents*) {}
   static void SetTabForWebContents(WebContents* contents,
                                    TabAndroid* tab_android) {
     content::WebContentsUserData<TabAndroidHelper>::CreateForWebContents(
@@ -112,14 +111,15 @@
     content::WebContentsUserData<TabAndroidHelper>::FromWebContents(contents)
         ->tab_android_ = tab_android;
   }
+
   static TabAndroid* FromWebContents(const WebContents* contents) {
-    if (TabAndroidHelper* helper = static_cast<TabAndroidHelper*>(
-            contents->GetUserData(UserDataKey()))) {
-      return helper->tab_android_;
-    }
-    return nullptr;
+    TabAndroidHelper* helper =
+        static_cast<TabAndroidHelper*>(contents->GetUserData(UserDataKey()));
+    return helper ? helper->tab_android_ : nullptr;
   }
 
+  explicit TabAndroidHelper(content::WebContents*) {}
+
  private:
   friend class content::WebContentsUserData<TabAndroidHelper>;
 
@@ -193,7 +193,7 @@
  private:
   // The |manager_| and |render_frame_host_| outlive this class.
   content::RenderFrameHost* const render_frame_host_;
-  TabAndroid* tab_;
+  TabAndroid* const tab_;
   mojo::Binding<blink::mojom::MediaDownloadInProductHelp> binding_;
 };
 
@@ -216,7 +216,7 @@
     : weak_java_tab_(env, obj),
       session_window_id_(SessionID::InvalidValue()),
       content_layer_(cc::Layer::Create()),
-      tab_content_manager_(NULL),
+      tab_content_manager_(nullptr),
       synced_tab_delegate_(new browser_sync::SyncedTabDelegateAndroid(this)),
       picture_in_picture_enabled_(false),
       embedded_media_experience_enabled_(false),
@@ -271,7 +271,7 @@
 
 Profile* TabAndroid::GetProfile() const {
   if (!web_contents())
-    return NULL;
+    return nullptr;
 
   return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
 }
@@ -299,40 +299,42 @@
 }
 
 void TabAndroid::HandlePopupNavigation(NavigateParams* params) {
-  DCHECK(params->source_contents == web_contents());
+  DCHECK_EQ(params->source_contents, web_contents());
   DCHECK(!params->contents_to_insert);
   DCHECK(!params->switch_to_singleton_tab);
 
   WindowOpenDisposition disposition = params->disposition;
   const GURL& url = params->url;
 
-  if (disposition == WindowOpenDisposition::NEW_POPUP ||
-      disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
-      disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
-      disposition == WindowOpenDisposition::NEW_WINDOW ||
-      disposition == WindowOpenDisposition::OFF_THE_RECORD) {
-    JNIEnv* env = AttachCurrentThread();
-    ScopedJavaLocalRef<jobject> jobj = weak_java_tab_.get(env);
-    ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(env, url.spec()));
-    ScopedJavaLocalRef<jstring> jheaders(
-        ConvertUTF8ToJavaString(env, params->extra_headers));
-    ScopedJavaLocalRef<jstring> jinitiator_origin;
-    if (params->initiator_origin) {
-      jinitiator_origin =
-          ConvertUTF8ToJavaString(env, params->initiator_origin->Serialize());
-    }
-    ScopedJavaLocalRef<jobject> jpost_data;
-    if (params->uses_post && params->post_data) {
-      jpost_data = content::ConvertResourceRequestBodyToJavaObject(
-          env, params->post_data);
-    }
-    Java_Tab_openNewTab(env, jobj, jurl, jinitiator_origin, jheaders,
-                        jpost_data, static_cast<int>(disposition),
-                        params->created_with_opener,
-                        params->is_renderer_initiated);
-  } else {
+  bool supported = disposition == WindowOpenDisposition::NEW_POPUP ||
+                   disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
+                   disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
+                   disposition == WindowOpenDisposition::NEW_WINDOW ||
+                   disposition == WindowOpenDisposition::OFF_THE_RECORD;
+  if (!supported) {
     NOTIMPLEMENTED();
+    return;
   }
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> jobj = weak_java_tab_.get(env);
+  ScopedJavaLocalRef<jstring> jurl(ConvertUTF8ToJavaString(env, url.spec()));
+  ScopedJavaLocalRef<jstring> jheaders(
+      ConvertUTF8ToJavaString(env, params->extra_headers));
+  ScopedJavaLocalRef<jstring> jinitiator_origin;
+  if (params->initiator_origin) {
+    jinitiator_origin =
+        ConvertUTF8ToJavaString(env, params->initiator_origin->Serialize());
+  }
+  ScopedJavaLocalRef<jobject> jpost_data;
+  if (params->uses_post && params->post_data) {
+    jpost_data =
+        content::ConvertResourceRequestBodyToJavaObject(env, params->post_data);
+  }
+  Java_Tab_openNewTab(env, jobj, jurl, jinitiator_origin, jheaders, jpost_data,
+                      static_cast<int>(disposition),
+                      params->created_with_opener,
+                      params->is_renderer_initiated);
 }
 
 bool TabAndroid::HasPrerenderedUrl(GURL gurl) {
@@ -343,9 +345,8 @@
   std::vector<content::WebContents*> contents =
       prerender_manager->GetAllPrerenderingContents();
   prerender::PrerenderContents* prerender_contents;
-  for (size_t i = 0; i < contents.size(); ++i) {
-    prerender_contents = prerender_manager->
-        GetPrerenderContents(contents.at(i));
+  for (content::WebContents* content : contents) {
+    prerender_contents = prerender_manager->GetPrerenderContents(content);
     if (prerender_contents->prerender_url() == gurl &&
         prerender_contents->has_finished_loading()) {
       return true;
@@ -461,7 +462,7 @@
   if (favicon_driver)
     favicon_driver->RemoveObserver(this);
 
-  web_contents()->SetDelegate(NULL);
+  web_contents()->SetDelegate(nullptr);
 
   if (delete_native) {
     // Terminate the renderer process if this is the last tab.
@@ -644,13 +645,7 @@
   printing::PrintViewManagerBasic::CreateForWebContents(contents);
   printing::PrintViewManagerBasic* print_view_manager =
       printing::PrintViewManagerBasic::FromWebContents(contents);
-  if (!print_view_manager)
-    return false;
-
-  if (!print_view_manager->PrintNow(rfh))
-    return false;
-
-  return true;
+  return print_view_manager && print_view_manager->PrintNow(rfh);
 }
 
 void TabAndroid::SetPendingPrint(int render_process_id, int render_frame_id) {
@@ -692,7 +687,7 @@
 prerender::PrerenderManager* TabAndroid::GetPrerenderManager() const {
   Profile* profile = GetProfile();
   if (!profile)
-    return NULL;
+    return nullptr;
   return prerender::PrerenderManagerFactory::GetForBrowserContext(profile);
 }
 
@@ -779,10 +774,10 @@
   std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded);
 
   // Return the first node matching the search criteria.
-  for (size_t i = 0; i < nodes.size(); ++i) {
-    if (only_editable && !managed->CanBeEditedByUser(nodes[i]))
+  for (const auto* node : nodes) {
+    if (only_editable && !managed->CanBeEditedByUser(node))
       continue;
-    return nodes[i]->id();
+    return node->id();
   }
 
   return -1;
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index de66325a..2bc8367 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -11,6 +11,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/command_line.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/android/feature_utilities.h"
@@ -56,7 +57,6 @@
 #include "content/public/browser/security_style_explanations.h"
 #include "content/public/browser/web_contents.h"
 #include "jni/TabWebContentsDelegateAndroid_jni.h"
-#include "ppapi/buildflags/buildflags.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -105,10 +105,15 @@
 
 void ShowFramebustBlockInfobarInternal(content::WebContents* web_contents,
                                        const GURL& url) {
+  auto intervention_outcome =
+      [](FramebustBlockMessageDelegate::InterventionOutcome outcome) {
+        UMA_HISTOGRAM_ENUMERATION("WebCore.Framebust.InterventionOutcome",
+                                  outcome);
+      };
   FramebustBlockInfoBar::Show(
       web_contents,
       std::make_unique<FramebustBlockMessageDelegate>(
-          web_contents, url, FramebustBlockMessageDelegate::OutcomeCallback()));
+          web_contents, url, base::BindOnce(intervention_outcome)));
 }
 
 }  // anonymous namespace
diff --git a/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc b/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc
index 80275db..406cfb1 100644
--- a/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_captured_sites_interactive_uitest.cc
@@ -107,8 +107,8 @@
   // TestRecipeReplayChromeFeatureActionExecutor
   bool AutofillForm(content::RenderFrameHost* frame,
                     const std::string& focus_element_css_selector,
-                    const int attempts = 1,
-                    const gfx::Point& offset = gfx::Point(0, 0)) override {
+                    const std::vector<std::string> iframe_path,
+                    const int attempts = 1) override {
     content::WebContents* web_contents =
         content::WebContents::FromRenderFrameHost(frame);
     AutofillManager* autofill_manager =
@@ -122,7 +122,8 @@
       tries++;
       autofill_manager->client()->HideAutofillPopup();
 
-      if (!ShowAutofillSuggestion(frame, focus_element_css_selector, offset)) {
+      if (!ShowAutofillSuggestion(frame, focus_element_css_selector,
+                                  iframe_path)) {
         LOG(WARNING) << "Failed to bring up the autofill suggestion drop down.";
         continue;
       }
@@ -247,25 +248,23 @@
  private:
   bool ShowAutofillSuggestion(content::RenderFrameHost* frame,
                               const std::string& target_element_xpath,
-                              const gfx::Point& offset = gfx::Point(0, 0)) {
+                              const std::vector<std::string> iframe_path) {
     // First, automation should focus on the frame containg the autofill form.
     // Doing so ensures that Chrome scrolls the element into view if the
     // element is off the page.
     if (!captured_sites_test_utils::TestRecipeReplayer::PlaceFocusOnElement(
-            frame, target_element_xpath, offset))
+            frame, target_element_xpath, iframe_path))
       return false;
 
-    int x, y;
+    gfx::Rect rect;
     if (!captured_sites_test_utils::TestRecipeReplayer::
-            GetCenterCoordinateOfTargetElement(frame, target_element_xpath, x,
-                                               y))
+            GetBoundingRectOfTargetElement(frame, target_element_xpath,
+                                           iframe_path, &rect))
       return false;
-    x += offset.x();
-    y += offset.y();
 
     test_delegate()->Reset();
     if (!captured_sites_test_utils::TestRecipeReplayer::
-            SimulateLeftMouseClickAt(frame, gfx::Point(x, y)))
+            SimulateLeftMouseClickAt(frame, rect.CenterPoint()))
       return false;
 
     return test_delegate()->Wait({ObservedUiEvents::kSuggestionShown},
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 2b5629f8..9c9f8b2b 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -639,8 +639,8 @@
   if (!GetTargetFrameFromAction(action, &frame))
     return false;
 
-  gfx::Point offset;
-  if (!GetTargetFrameCoordOffsetFromAction(action, &offset))
+  std::vector<std::string> frame_path;
+  if (!GetIFramePathFromAction(action, &frame_path))
     return false;
 
   if (!WaitForElementToBeReady(frame, xpath, visibility_enum_val))
@@ -661,8 +661,8 @@
     return false;
   }
 
-  if (!feature_action_executor()->AutofillForm(
-          frame, xpath, kAutofillActionNumRetries, offset))
+  if (!feature_action_executor()->AutofillForm(frame, xpath, frame_path,
+                                               kAutofillActionNumRetries))
     return false;
   page_activity_observer.WaitTillPageIsIdle(
       kAutofillActionWaitForVisualUpdateTimeout);
@@ -719,11 +719,11 @@
   VLOG(1) << "Hovering over `" << xpath << "`.";
   PageActivityObserver page_activity_observer(frame);
 
-  int x, y;
-  if (!GetCenterCoordinateOfTargetElement(frame, xpath, x, y))
+  gfx::Rect rect;
+  if (!GetBoundingRectOfTargetElement(frame, xpath, &rect))
     return false;
 
-  if (!SimulateMouseHoverAt(frame, gfx::Point(x, y)))
+  if (!SimulateMouseHoverAt(frame, rect.CenterPoint()))
     return false;
 
   if (!page_activity_observer.WaitForVisualUpdate()) {
@@ -749,8 +749,8 @@
   if (!GetTargetFrameFromAction(action, &frame))
     return false;
 
-  gfx::Point offset;
-  if (!GetTargetFrameCoordOffsetFromAction(action, &offset))
+  std::vector<std::string> frame_path;
+  if (!GetIFramePathFromAction(action, &frame_path))
     return false;
 
   if (!WaitForElementToBeReady(frame, xpath, visibility_enum_val))
@@ -758,7 +758,7 @@
 
   VLOG(1) << "Press 'Enter' on `" << xpath << "`.";
   PageActivityObserver page_activity_observer(frame);
-  if (!PlaceFocusOnElement(frame, xpath, offset))
+  if (!PlaceFocusOnElement(frame, xpath, frame_path))
     return false;
 
   ui::DomKey key = ui::DomKey::ENTER;
@@ -950,8 +950,8 @@
   if (!GetTargetFrameFromAction(action, &frame))
     return false;
 
-  gfx::Point offset;
-  if (!GetTargetFrameCoordOffsetFromAction(action, &offset))
+  std::vector<std::string> frame_path;
+  if (!GetIFramePathFromAction(action, &frame_path))
     return false;
 
   if (!WaitForElementToBeReady(frame, xpath, visibility_enum_val))
@@ -979,7 +979,7 @@
     return false;
   }
 
-  if (!PlaceFocusOnElement(frame, xpath, offset))
+  if (!PlaceFocusOnElement(frame, xpath, frame_path))
     return false;
 
   VLOG(1) << "Typing '" << value << "' inside `" << xpath << "`.";
@@ -1241,9 +1241,11 @@
   return true;
 }
 
-bool TestRecipeReplayer::GetTargetFrameCoordOffsetFromAction(
+bool TestRecipeReplayer::GetIFramePathFromAction(
     const base::DictionaryValue& action,
-    gfx::Point* point) {
+    std::vector<std::string>* iframe_path) {
+  *iframe_path = std::vector<std::string>();
+
   const base::Value* iframe_container = action.FindKey("context");
   if (!iframe_container) {
     ADD_FAILURE() << "Failed to extract the iframe context from action!";
@@ -1256,38 +1258,60 @@
     return false;
   }
 
-  const base::Value* offset_container = iframe->FindKey("offset");
-  if (!offset_container) {
-    // By default, the frame offset is (0, 0).
-    *point = gfx::Point(0, 0);
+  const base::Value* iframe_path_container = iframe->FindKey("path");
+  if (!iframe_path_container) {
+    // If the action does not have a path container, it would mean that:
+    // 1. The target frame is the top level frame.
+    // 2. The target frame is an iframe, but it is the top-level frame in its
+    //    rendering process.
     return true;
   }
 
-  const base::DictionaryValue* offset;
-  if (!offset_container->GetAsDictionary(&offset)) {
-    ADD_FAILURE() << "Failed to extract the iframe offset object!";
+  if (base::Value::Type::LIST != iframe_path_container->type()) {
+    ADD_FAILURE() << "The action's iframe path is not a list!";
     return false;
   }
 
-  int x, y;
-
-  const base::Value* x_container = offset->FindKey("x");
-  if (base::Value::Type::INTEGER != x_container->type()) {
-    ADD_FAILURE() << "Offset x property is not an integer!";
-    return false;
+  const base::Value::ListStorage& iframe_xpath_list =
+      iframe_path_container->GetList();
+  for (auto it_xpath = iframe_xpath_list.begin();
+       it_xpath != iframe_xpath_list.end(); ++it_xpath) {
+    std::string xpath;
+    if (!it_xpath->GetAsString(&xpath)) {
+      ADD_FAILURE() << "Failed to extract the iframe xpath from action!";
+      return false;
+    }
+    iframe_path->push_back(xpath);
   }
 
-  x = x_container->GetInt();
+  return true;
+}
 
-  const base::Value* y_container = offset->FindKey("y");
-  if (base::Value::Type::INTEGER != y_container->type()) {
-    ADD_FAILURE() << "Offset y property is not an integer!";
-    return false;
+bool TestRecipeReplayer::GetIFrameOffsetFromIFramePath(
+    content::RenderFrameHost* frame,
+    const std::vector<std::string>& iframe_path,
+    gfx::Vector2d* offset) {
+  *offset = gfx::Vector2d(0, 0);
+
+  for (auto it_xpath = iframe_path.begin(); it_xpath != iframe_path.end();
+       it_xpath++) {
+    content::RenderFrameHost* parent_frame = frame->GetParent();
+    if (parent_frame == nullptr) {
+      ADD_FAILURE() << "Trying to iterate past the top level frame!";
+      return false;
+    }
+
+    gfx::Rect rect;
+    if (!GetBoundingRectOfTargetElement(parent_frame, *it_xpath, &rect)) {
+      ADD_FAILURE() << "Failed to extract position of iframe with xpath `"
+                    << *it_xpath << "`!";
+      return false;
+    }
+
+    *offset += rect.OffsetFromOrigin();
+    frame = parent_frame;
   }
 
-  y = y_container->GetInt();
-
-  *point = gfx::Point(x, y);
   return true;
 }
 
@@ -1395,9 +1419,10 @@
   return false;
 }
 
-bool TestRecipeReplayer::PlaceFocusOnElement(content::RenderFrameHost* frame,
-                                             const std::string& element_xpath,
-                                             const gfx::Point& offset) {
+bool TestRecipeReplayer::PlaceFocusOnElement(
+    content::RenderFrameHost* frame,
+    const std::string& element_xpath,
+    const std::vector<std::string> iframe_path) {
   const std::string focus_on_target_field_js(base::StringPrintf(
       "try {"
       "  function onFocusHandler(event) {"
@@ -1433,63 +1458,97 @@
   } else {
     // Failing focusing on an element through script, use the less preferred
     // method of left mouse clicking the element.
-    int x, y;
-    if (!GetCenterCoordinateOfTargetElement(frame, element_xpath, x, y))
+    gfx::Rect rect;
+    if (!GetBoundingRectOfTargetElement(frame, element_xpath, iframe_path,
+                                        &rect))
       return false;
 
-    return SimulateLeftMouseClickAt(frame, gfx::Point(x, y));
+    return SimulateLeftMouseClickAt(frame, rect.CenterPoint());
   }
 }
 
-bool TestRecipeReplayer::GetCenterCoordinateOfTargetElement(
+bool TestRecipeReplayer::GetBoundingRectOfTargetElement(
     content::RenderFrameHost* frame,
     const std::string& target_element_xpath,
-    int& x,
-    int& y) {
-  const std::string get_target_field_x_js(base::StringPrintf(
+    gfx::Rect* output_rect) {
+  std::string rect_str;
+  const std::string get_element_bounding_rect_js(base::StringPrintf(
       "window.domAutomationController.send("
       "    (function() {"
       "       try {"
       "         const element = automation_helper.getElementByXpath(`%s`);"
       "         const rect = element.getBoundingClientRect();"
-      "         return Math.floor(rect.left + rect.width / 2);"
+      "         return Math.round(rect.left) + ',' + "
+      "                Math.round(rect.top) + ',' + "
+      "                Math.round(rect.width) + ',' + "
+      "                Math.round(rect.height);"
       "       } catch(ex) {}"
-      "       return -1;"
+      "       return '';"
       "    })());",
       target_element_xpath.c_str()));
-  const std::string get_target_field_y_js(base::StringPrintf(
-      "window.domAutomationController.send("
-      "    (function() {"
-      "       try {"
-      "         const element = automation_helper.getElementByXpath(`%s`);"
-      "         const rect = element.getBoundingClientRect();"
-      "         return Math.floor(rect.top + rect.height / 2);"
-      "       } catch(ex) {}"
-      "       return -1;"
-      "    })());",
-      target_element_xpath.c_str()));
-  if (!content::ExecuteScriptAndExtractInt(frame, get_target_field_x_js, &x)) {
+
+  if (!content::ExecuteScriptAndExtractString(
+          frame, get_element_bounding_rect_js, &rect_str)) {
     ADD_FAILURE()
-        << "Failed to run script to extract target element's x coordinate!";
+        << "Failed to run script to extract target element's bounding rect!";
     return false;
   }
 
-  if (x == -1) {
-    ADD_FAILURE() << "Failed to extract target element's x coordinate!";
+  if (rect_str.empty()) {
+    ADD_FAILURE() << "Failed to extract target element's bounding rect!";
     return false;
   }
 
-  if (!content::ExecuteScriptAndExtractInt(frame, get_target_field_y_js, &y)) {
-    ADD_FAILURE()
-        << "Failed to run script to extract target element's y coordinate!";
+  // Parse the bounding rect string to extract the element coordinates.
+  std::istringstream rect_stream(rect_str);
+  std::string token;
+  if (!std::getline(rect_stream, token, ',')) {
+    ADD_FAILURE() << "Failed to extract target element's x coordinate from "
+                  << "the string `" << rect_str << "`!";
     return false;
   }
 
-  if (y == -1) {
-    ADD_FAILURE() << "Failed to extract target element's y coordinate!";
+  output_rect->set_x(std::stoi(token));
+
+  if (!std::getline(rect_stream, token, ',')) {
+    ADD_FAILURE() << "Failed to extract target element's y coordinate from "
+                  << "the string `" << rect_str << "`!";
     return false;
   }
 
+  output_rect->set_y(std::stoi(token));
+
+  if (!std::getline(rect_stream, token, ',')) {
+    ADD_FAILURE() << "Failed to extract target element's width from "
+                  << "the string `" << rect_str << "`!";
+    return false;
+  }
+
+  output_rect->set_width(std::stoi(token));
+
+  if (!std::getline(rect_stream, token, ',')) {
+    ADD_FAILURE() << "Failed to extract target element's height from "
+                  << "the string `" << rect_str << "`!";
+    return false;
+  }
+
+  output_rect->set_height(std::stoi(token));
+
+  return true;
+}
+
+bool TestRecipeReplayer::GetBoundingRectOfTargetElement(
+    content::RenderFrameHost* frame,
+    const std::string& target_element_xpath,
+    const std::vector<std::string> iframe_path,
+    gfx::Rect* output_rect) {
+  gfx::Vector2d offset;
+  if (!GetIFrameOffsetFromIFramePath(frame, iframe_path, &offset))
+    return false;
+  if (!GetBoundingRectOfTargetElement(frame, target_element_xpath, output_rect))
+    return false;
+
+  *output_rect += offset;
   return true;
 }
 
@@ -1696,8 +1755,8 @@
 bool TestRecipeReplayChromeFeatureActionExecutor::AutofillForm(
     content::RenderFrameHost* frame,
     const std::string& focus_element_css_selector,
-    const int attempts,
-    const gfx::Point& offset) {
+    const std::vector<std::string> iframe_path,
+    const int attempts) {
   ADD_FAILURE() << "TestRecipeReplayChromeFeatureActionExecutor::AutofillForm "
                    "is not implemented!";
   return false;
diff --git a/chrome/browser/autofill/captured_sites_test_utils.h b/chrome/browser/autofill/captured_sites_test_utils.h
index a7c964c9..b3d7bbf 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.h
+++ b/chrome/browser/autofill/captured_sites_test_utils.h
@@ -154,8 +154,8 @@
   // document.
   virtual bool AutofillForm(content::RenderFrameHost* frame,
                             const std::string& focus_element_css_selector,
-                            const int attempts = 1,
-                            const gfx::Point& offset = gfx::Point(0, 0));
+                            const std::vector<std::string> iframe_path,
+                            const int attempts = 1);
   virtual bool AddAutofillProfileInfo(const std::string& field_type,
                                       const std::string& field_value);
   virtual bool SetupAutofillProfile();
@@ -214,12 +214,12 @@
   static void SetUpCommandLine(base::CommandLine* command_line);
   static bool PlaceFocusOnElement(content::RenderFrameHost* frame,
                                   const std::string& element_xpath,
-                                  const gfx::Point& offset);
-  static bool GetCenterCoordinateOfTargetElement(
+                                  const std::vector<std::string> iframe_path);
+  static bool GetBoundingRectOfTargetElement(
       content::RenderFrameHost* frame,
       const std::string& target_element_xpath,
-      int& x,
-      int& y);
+      const std::vector<std::string> iframe_path,
+      gfx::Rect* output_rect);
   static bool SimulateLeftMouseClickAt(
       content::RenderFrameHost* render_frame_host,
       const gfx::Point& point);
@@ -227,6 +227,15 @@
                                    const gfx::Point& point);
 
  private:
+  static bool GetIFrameOffsetFromIFramePath(
+      content::RenderFrameHost* frame,
+      const std::vector<std::string>& iframe_path,
+      gfx::Vector2d* offset);
+  static bool GetBoundingRectOfTargetElement(
+      content::RenderFrameHost* frame,
+      const std::string& target_element_xpath,
+      gfx::Rect* output_rect);
+
   Browser* browser();
   TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor();
   content::WebContents* GetWebContents();
@@ -264,8 +273,8 @@
                                            std::string* xpath);
   bool GetTargetFrameFromAction(const base::DictionaryValue& action,
                                 content::RenderFrameHost** frame);
-  bool GetTargetFrameCoordOffsetFromAction(const base::DictionaryValue& action,
-                                           gfx::Point* point);
+  bool GetIFramePathFromAction(const base::DictionaryValue& action,
+                               std::vector<std::string>* iframe_path);
   bool GetTargetHTMLElementVisibilityEnumFromAction(
       const base::DictionaryValue& action,
       int* visibility_enum_val);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 7e944ee..a828f9f 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -55,7 +55,7 @@
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/font_family_cache.h"
-#include "chrome/browser/language/chrome_language_detection_tab_helper.h"
+#include "chrome/browser/language/translate_frame_binder.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/loader/chrome_navigation_data.h"
 #include "chrome/browser/media/router/media_router_feature.h"
@@ -4328,8 +4328,8 @@
           content::RenderProcessHost*, const url::Origin&>>();
 
   // Register mojo ContentTranslateDriver interface only for main frame.
-  frame_interfaces_parameterized_->AddInterface(base::BindRepeating(
-      &ChromeLanguageDetectionTabHelper::BindContentTranslateDriver));
+  frame_interfaces_parameterized_->AddInterface(
+      base::BindRepeating(&language::BindContentTranslateDriver));
 
   frame_interfaces_parameterized_->AddInterface(
       base::BindRepeating(&ChromePasswordManagerClient::BindCredentialManager));
diff --git a/chrome/browser/language/chrome_language_detection_tab_helper.cc b/chrome/browser/language/chrome_language_detection_tab_helper.cc
deleted file mode 100644
index e9950afd..0000000
--- a/chrome/browser/language/chrome_language_detection_tab_helper.cc
+++ /dev/null
@@ -1,68 +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 "chrome/browser/language/chrome_language_detection_tab_helper.h"
-
-#include "base/logging.h"
-#include "chrome/browser/language/url_language_histogram_factory.h"
-#include "chrome/browser/translate/chrome_translate_client.h"
-#include "components/language/core/browser/url_language_histogram.h"
-#include "components/translate/core/common/language_detection_details.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/browser/web_contents.h"
-
-ChromeLanguageDetectionTabHelper::ChromeLanguageDetectionTabHelper(
-    content::WebContents* const web_contents)
-    : content::WebContentsObserver(web_contents),
-      language_histogram_(UrlLanguageHistogramFactory::GetForBrowserContext(
-          web_contents->GetBrowserContext())) {}
-
-ChromeLanguageDetectionTabHelper::~ChromeLanguageDetectionTabHelper() = default;
-
-// static
-void ChromeLanguageDetectionTabHelper::BindContentTranslateDriver(
-    translate::mojom::ContentTranslateDriverRequest request,
-    content::RenderFrameHost* const render_frame_host) {
-  // Only valid for the main frame.
-  if (render_frame_host->GetParent())
-    return;
-
-  content::WebContents* const web_contents =
-      content::WebContents::FromRenderFrameHost(render_frame_host);
-  if (!web_contents)
-    return;
-
-  // We try to bind to the driver, but if driver is not ready for now or totally
-  // not available for this render frame host, the request will be just dropped.
-  // This would cause the message pipe to be closed, which will raise a
-  // connection error on the peer side.
-  ChromeLanguageDetectionTabHelper* const instance =
-      ChromeLanguageDetectionTabHelper::FromWebContents(web_contents);
-  if (!instance)
-    return;
-
-  instance->bindings_.AddBinding(instance, std::move(request));
-}
-
-// translate::mojom::ContentTranslateDriver implementation.
-void ChromeLanguageDetectionTabHelper::RegisterPage(
-    translate::mojom::PagePtr page,
-    const translate::LanguageDetectionDetails& details,
-    const bool page_needs_translation) {
-  // If we have a language histogram (i.e. we're not in incognito), update it
-  // with the detected language of every page visited.
-  if (language_histogram_ && details.is_cld_reliable)
-    language_histogram_->OnPageVisited(details.cld_language);
-
-  ChromeTranslateClient* const translate_client =
-      ChromeTranslateClient::FromWebContents(web_contents());
-  if (!translate_client)
-    return;
-
-  translate_client->translate_driver().OnPageReady(std::move(page), details,
-                                                   page_needs_translation);
-}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(ChromeLanguageDetectionTabHelper)
diff --git a/chrome/browser/language/chrome_language_detection_tab_helper.h b/chrome/browser/language/chrome_language_detection_tab_helper.h
deleted file mode 100644
index 1a2de2c..0000000
--- a/chrome/browser/language/chrome_language_detection_tab_helper.h
+++ /dev/null
@@ -1,59 +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 CHROME_BROWSER_LANGUAGE_CHROME_LANGUAGE_DETECTION_TAB_HELPER_H_
-#define CHROME_BROWSER_LANGUAGE_CHROME_LANGUAGE_DETECTION_TAB_HELPER_H_
-
-#include "base/feature_list.h"
-#include "base/macros.h"
-#include "components/translate/content/common/translate.mojom.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/bind_source_info.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-namespace language {
-class UrlLanguageHistogram;
-}  // namespace language
-
-// Dispatches language detection messages from render frames to language and
-// translate components.
-class ChromeLanguageDetectionTabHelper
-    : public content::WebContentsObserver,
-      public content::WebContentsUserData<ChromeLanguageDetectionTabHelper>,
-      public translate::mojom::ContentTranslateDriver {
- public:
-  ~ChromeLanguageDetectionTabHelper() override;
-
-  static void BindContentTranslateDriver(
-      translate::mojom::ContentTranslateDriverRequest request,
-      content::RenderFrameHost* render_frame_host);
-
-  // translate::mojom::ContentTranslateDriver implementation.
-  void RegisterPage(translate::mojom::PagePtr page,
-                    const translate::LanguageDetectionDetails& details,
-                    bool page_needs_translation) override;
-
- private:
-  explicit ChromeLanguageDetectionTabHelper(content::WebContents* web_contents);
-  friend class content::WebContentsUserData<ChromeLanguageDetectionTabHelper>;
-
-  // Histogram to be notified about detected language of every page visited. Not
-  // owned here.
-  language::UrlLanguageHistogram* const language_histogram_;
-
-  // ChromeLanguageDetectionTabHelper is a singleton per web contents, serving
-  // for multiple render frames.
-  mojo::BindingSet<translate::mojom::ContentTranslateDriver> bindings_;
-
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeLanguageDetectionTabHelper);
-};
-
-#endif  // CHROME_BROWSER_LANGUAGE_CHROME_LANGUAGE_DETECTION_TAB_HELPER_H_
diff --git a/chrome/browser/language/translate_frame_binder.cc b/chrome/browser/language/translate_frame_binder.cc
new file mode 100644
index 0000000..b32ff94
--- /dev/null
+++ b/chrome/browser/language/translate_frame_binder.cc
@@ -0,0 +1,34 @@
+// 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/language/translate_frame_binder.h"
+
+#include "chrome/browser/translate/chrome_translate_client.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+
+namespace language {
+
+void BindContentTranslateDriver(
+    translate::mojom::ContentTranslateDriverRequest request,
+    content::RenderFrameHost* render_frame_host) {
+  // Only valid for the main frame.
+  if (render_frame_host->GetParent())
+    return;
+
+  content::WebContents* const web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host);
+  if (!web_contents)
+    return;
+
+  ChromeTranslateClient* const translate_client =
+      ChromeTranslateClient::FromWebContents(web_contents);
+  if (!translate_client)
+    return;
+
+  translate_client->translate_driver().AddBinding(std::move(request));
+}
+
+}  // namespace language
diff --git a/chrome/browser/language/translate_frame_binder.h b/chrome/browser/language/translate_frame_binder.h
new file mode 100644
index 0000000..59025fc
--- /dev/null
+++ b/chrome/browser/language/translate_frame_binder.h
@@ -0,0 +1,22 @@
+// 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_LANGUAGE_TRANSLATE_FRAME_BINDER_H_
+#define CHROME_BROWSER_LANGUAGE_TRANSLATE_FRAME_BINDER_H_
+
+#include "components/translate/content/common/translate.mojom.h"
+
+namespace content {
+class RenderFrameHost;
+}
+
+namespace language {
+
+void BindContentTranslateDriver(
+    translate::mojom::ContentTranslateDriverRequest request,
+    content::RenderFrameHost* render_frame_host);
+
+}
+
+#endif  // CHROME_BROWSER_LANGUAGE_TRANSLATE_FRAME_BINDER_H_
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index c4c8736..9ad39d1 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -386,8 +386,8 @@
   contents->GetMainFrame()->ExecuteJavaScriptForTests(
       base::ASCIIToUTF16("createAdFrame('frame_factory.html', '');"));
   // Two pages subresources should have been reported as ad. The iframe resource
-  // should also be reported as an ad.
-  waiter->AddMinimumAdResourceExpectation(5);
+  // and its three subresources should also be reported as ads.
+  waiter->AddMinimumAdResourceExpectation(6);
   waiter->Wait();
 }
 
@@ -408,14 +408,14 @@
       embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
   contents->GetMainFrame()->ExecuteJavaScriptForTests(
       base::ASCIIToUTF16("createAdFrame('frame_factory.html', 'test');"));
-  waiter->AddMinimumAdResourceExpectation(5);
+  waiter->AddMinimumAdResourceExpectation(6);
   waiter->Wait();
   NavigateIframeToURL(
       web_contents(), "test",
       embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
-  // The new subframe as well as two of its page subresources should be reported
-  // as an ad.
-  waiter->AddMinimumAdResourceExpectation(8);
+  // The new subframe and its three subresources should be reported
+  // as ads.
+  waiter->AddMinimumAdResourceExpectation(10);
   waiter->Wait();
 }
 
diff --git a/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc b/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc
index e9612f8..951d2c65 100644
--- a/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc
+++ b/chrome/browser/payments/service_worker_payment_app_factory_browsertest.cc
@@ -45,7 +45,17 @@
         bobpay_(net::EmbeddedTestServer::TYPE_HTTPS),
         frankpay_(net::EmbeddedTestServer::TYPE_HTTPS),
         georgepay_(net::EmbeddedTestServer::TYPE_HTTPS),
-        kylepay_(net::EmbeddedTestServer::TYPE_HTTPS) {
+        kylepay_(net::EmbeddedTestServer::TYPE_HTTPS),
+        larrypay_(net::EmbeddedTestServer::TYPE_HTTPS),
+        charlie_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        david_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        frank_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        george_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        harry_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        ike_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        john_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        kyle_example_(net::EmbeddedTestServer::TYPE_HTTPS),
+        larry_example_(net::EmbeddedTestServer::TYPE_HTTPS) {
     scoped_feature_list_.InitWithFeatures(
         {::features::kServiceWorkerPaymentApps,
          features::kWebPaymentsJustInTimePaymentApp},
@@ -73,6 +83,16 @@
     ASSERT_TRUE(StartTestServer("frankpay.com", &frankpay_));
     ASSERT_TRUE(StartTestServer("georgepay.com", &georgepay_));
     ASSERT_TRUE(StartTestServer("kylepay.com", &kylepay_));
+    ASSERT_TRUE(StartTestServer("larrypay.com", &larrypay_));
+    ASSERT_TRUE(StartTestServer("charlie.example.com", &charlie_example_));
+    ASSERT_TRUE(StartTestServer("david.example.com", &david_example_));
+    ASSERT_TRUE(StartTestServer("frank.example.com", &frank_example_));
+    ASSERT_TRUE(StartTestServer("george.example.com", &george_example_));
+    ASSERT_TRUE(StartTestServer("harry.example.com", &harry_example_));
+    ASSERT_TRUE(StartTestServer("ike.example.com", &ike_example_));
+    ASSERT_TRUE(StartTestServer("john.example.com", &john_example_));
+    ASSERT_TRUE(StartTestServer("kyle.example.com", &kyle_example_));
+    ASSERT_TRUE(StartTestServer("larry.example.com", &larry_example_));
 
     GetPermissionRequestManager()->set_auto_response_for_test(
         PermissionRequestManager::ACCEPT_ALL);
@@ -126,8 +146,35 @@
                                  georgepay_.GetURL("georgepay.com", "/"));
     downloader->AddTestServerURL("https://kylepay.com/",
                                  kylepay_.GetURL("kylepay.com", "/"));
+    downloader->AddTestServerURL("https://larrypay.com/",
+                                 larrypay_.GetURL("larrypay.com", "/"));
+    downloader->AddTestServerURL(
+        "https://charlie.example.com/",
+        charlie_example_.GetURL("charlie.example.com", "/"));
+    downloader->AddTestServerURL(
+        "https://david.example.com/",
+        david_example_.GetURL("david.example.com", "/"));
+    downloader->AddTestServerURL(
+        "https://frank.example.com/",
+        frank_example_.GetURL("frank.example.com", "/"));
+    downloader->AddTestServerURL(
+        "https://george.example.com/",
+        george_example_.GetURL("george.example.com", "/"));
+    downloader->AddTestServerURL(
+        "https://harry.example.com/",
+        harry_example_.GetURL("harry.example.com", "/"));
+    downloader->AddTestServerURL("https://ike.example.com/",
+                                 ike_example_.GetURL("ike.example.com", "/"));
+    downloader->AddTestServerURL("https://john.example.com/",
+                                 john_example_.GetURL("john.example.com", "/"));
+    downloader->AddTestServerURL("https://kyle.example.com/",
+                                 kyle_example_.GetURL("kyle.example.com", "/"));
+    downloader->AddTestServerURL(
+        "https://larry.example.com/",
+        larry_example_.GetURL("larry.example.com", "/"));
     ServiceWorkerPaymentAppFactory::GetInstance()
-        ->SetDownloaderAndIgnorePortInAppScopeForTesting(std::move(downloader));
+        ->SetDownloaderAndIgnorePortInOriginComparisonForTesting(
+            std::move(downloader));
 
     std::vector<mojom::PaymentMethodDataPtr> method_data;
     for (const auto& identifier : payment_method_identifiers) {
@@ -238,6 +285,46 @@
   // time."
   net::EmbeddedTestServer kylepay_;
 
+  // https://larrypay.com/webpay cross-site redirects to
+  // https://kylepay.com/webpay.
+  net::EmbeddedTestServer larrypay_;
+
+  // https://charlie.example.com/webpay same-site redirects to
+  // https://david.example.com/webpay.
+  net::EmbeddedTestServer charlie_example_;
+
+  // https://david.example.com/webpay same-site redirects to
+  // https://frank.example.com/webpay.
+  net::EmbeddedTestServer david_example_;
+
+  // https://frank.example.com/webpay same-site redirects to
+  // https://george.example.com/webpay.
+  net::EmbeddedTestServer frank_example_;
+
+  // https://george.example.com/webpay same-site redirects to
+  // https://harry.example.com/webpay.
+  net::EmbeddedTestServer george_example_;
+
+  // https://harry.example.com/webpay hosts a payment handler that can be
+  // installed "just in time."
+  net::EmbeddedTestServer harry_example_;
+
+  // https://ike.example.com/webpay has a cross-origin HTTP Link header to
+  // https://harry.example.com/payment-manifest.json.
+  net::EmbeddedTestServer ike_example_;
+
+  // https://john.example.com/payment-method.json has a cross-origin default
+  // application https://harry.example.com/app.json.
+  net::EmbeddedTestServer john_example_;
+
+  // https://kyle.example.com/app.json has a cross-origin service worker
+  // https://harry.example.com/app.js.
+  net::EmbeddedTestServer kyle_example_;
+
+  // https://larry.example.com/app.json has a cross-origin service worker scope
+  // https://harry.example.com/webpay/.
+  net::EmbeddedTestServer larry_example_;
+
   // The installed apps that have been found by the factory in
   // GetAllPaymentAppsForMethods() method.
   content::PaymentAppProvider::PaymentApps apps_;
@@ -514,4 +601,169 @@
   }
 }
 
+// The payment method https://larrypay.com/webpay is not valid, because it
+// redirects to a different site (https://kylepay.com/webpay).
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       InvalidDifferentSiteRedirect) {
+  {
+    GetAllPaymentAppsForMethods({"https://larrypay.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://larrypay.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+}
+
+// The payment method https://charlie.example.com/webpay is not valid, because
+// it redirects 4 times (charlie -> david -> frank -> george -> harry).
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       FourRedirectsIsNotValid) {
+  {
+    GetAllPaymentAppsForMethods({"https://charlie.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://charlie.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+}
+
+// The payment method https://david.example.com/webpay is valid, because it
+// redirects 3 times (david -> frank -> george -> harry).
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       ThreeRedirectsIsValid) {
+  {
+    GetAllPaymentAppsForMethods({"https://david.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    ASSERT_EQ(1U, installable_apps().size());
+    ExpectInstallablePaymentAppInScope("https://harry.example.com/webpay");
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://david.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    ASSERT_EQ(1U, installable_apps().size());
+    ExpectInstallablePaymentAppInScope("https://harry.example.com/webpay");
+  }
+}
+
+// The payment method https://george.example.com/webpay is valid, because it
+// redirects once (george -> harry).
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       OneRedirectIsValid) {
+  {
+    GetAllPaymentAppsForMethods({"https://george.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    ASSERT_EQ(1U, installable_apps().size());
+    ExpectInstallablePaymentAppInScope("https://harry.example.com/webpay");
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://george.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    ASSERT_EQ(1U, installable_apps().size());
+    ExpectInstallablePaymentAppInScope("https://harry.example.com/webpay");
+  }
+}
+
+// The payment method https://ike.example.com/webpay is not valid, because of
+// its cross-origin HTTP Link to
+// https://harry.example.com/payment-manifest.json.
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       CrossOriginHttpLinkHeaderIsInvalid) {
+  {
+    GetAllPaymentAppsForMethods({"https://ike.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://ike.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+}
+
+// The payment method https://john.example.com/webpay is not valid, because of
+// its cross-origin default application https://harry.example.com/app.json.
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       CrossOriginDefaultApplicationIsInvalid) {
+  {
+    GetAllPaymentAppsForMethods({"https://john.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://john.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+}
+
+// The payment method https://kyle.example.com/webpay is not valid, because of
+// its cross-origin service worker location https://harry.example.com/app.js.
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       CrossOriginServiceWorkerIsInvalid) {
+  {
+    GetAllPaymentAppsForMethods({"https://kyle.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://kyle.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+}
+
+// The payment method https://larry.example.com/webpay is not valid, because of
+// its cross-origin service worker scope https://harry.example.com/webpay/".
+IN_PROC_BROWSER_TEST_F(ServiceWorkerPaymentAppFactoryBrowserTest,
+                       CrossOriginServiceWorkerScopeIsInvalid) {
+  {
+    GetAllPaymentAppsForMethods({"https://larry.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+
+  // Repeat lookups should have identical results.
+  {
+    GetAllPaymentAppsForMethods({"https://larry.example.com/webpay"});
+
+    EXPECT_TRUE(apps().empty());
+    EXPECT_TRUE(installable_apps().empty());
+  }
+}
+
 }  // namespace payments
diff --git a/chrome/browser/previews/lazyload_browsertest.cc b/chrome/browser/previews/lazyload_browsertest.cc
new file mode 100644
index 0000000..1de73ea
--- /dev/null
+++ b/chrome/browser/previews/lazyload_browsertest.cc
@@ -0,0 +1,71 @@
+// 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/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test_base.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class LazyLoadBrowserTest : public InProcessBrowserTest {
+ protected:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(features::kLazyImageLoading);
+    InProcessBrowserTest::SetUp();
+  }
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, CSSBackgroundImageDeferred) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/lazyload/css-background-image.html"));
+
+  base::RunLoop().RunUntilIdle();
+  // Navigate away to finish the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  // Verify that nothing is recorded for the image bucket.
+  EXPECT_GE(0, histogram_tester.GetBucketCount(
+                   "DataUse.ContentType.UserTrafficKB",
+                   data_use_measurement::DataUseUserData::IMAGE));
+}
+
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+// Disable the tests flaky in Windows, chromeOS
+#define MAYBE_CSSBackgroundImageLoadedWhenScrolled \
+  DISABLED_CSSBackgroundImageLoadedWhenScrolled
+#else
+#define MAYBE_CSSBackgroundImageLoadedWhenScrolled \
+  CSSBackgroundImageLoadedWhenScrolled
+#endif
+
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest,
+                       MAYBE_CSSBackgroundImageLoadedWhenScrolled) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  base::HistogramTester histogram_tester;
+
+  // Simulate scrolling by loading the anchor section where images are located.
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "/lazyload/css-background-image.html#images"));
+
+  base::RunLoop().RunUntilIdle();
+  // Navigate away to finish the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  // Verify that the image bucket has substantial kilobytes recorded.
+  EXPECT_LE(35 /* kb */, histogram_tester.GetBucketCount(
+                             "DataUse.ContentType.UserTrafficKB",
+                             data_use_measurement::DataUseUserData::IMAGE));
+}
diff --git a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
index 44a9f6b..72e8a98 100644
--- a/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc
@@ -7,7 +7,6 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
 #include "chrome/common/pref_names.h"
@@ -39,8 +38,7 @@
   }
 
   void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
-    base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM);
-    InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line);
+    InProcessBrowserTest::SetUpDefaultCommandLine(command_line);
     command_line->AppendSwitch(
         switches::kEnableExperimentalAccessibilityLabels);
   }
@@ -73,18 +71,10 @@
 
 }  // namespace
 
-#if defined(OS_LINUX)
-#define MAYBE_AccessibilityLabelsNotShownWithoutScreenReader \
-  DISABLED_AccessibilityLabelsNotShownWithoutScreenReader
-#else
-#define MAYBE_AccessibilityLabelsNotShownWithoutScreenReader \
-  AccessibilityLabelsNotShownWithoutScreenReader
-#endif
 // Tests that opening a context menu does not show the menu option if a
 // screen reader is not enabled, regardless of the image labels setting.
-// TODO(crbug.com/921487) Investigate flakes on linux.
 IN_PROC_BROWSER_TEST_F(AccessibilityLabelsMenuObserverTest,
-                       MAYBE_AccessibilityLabelsNotShownWithoutScreenReader) {
+                       AccessibilityLabelsNotShownWithoutScreenReader) {
   menu()->GetPrefs()->SetBoolean(prefs::kAccessibilityImageLabelsEnabled,
                                  false);
   InitMenu();
@@ -125,16 +115,8 @@
   EXPECT_FALSE(item.hidden);
 }
 #else
-#if defined(OS_LINUX)
-#define MAYBE_AccessibilityLabelsShowWithScreenReaderEnabled \
-  DISABLED_AccessibilityLabelsShowWithScreenReaderEnabled
-#else
-#define MAYBE_AccessibilityLabelsShowWithScreenReaderEnabled \
-  AccessibilityLabelsShowWithScreenReaderEnabled
-#endif
-// TODO(crbug.com/921487) Investigate flakes on linux.
 IN_PROC_BROWSER_TEST_F(AccessibilityLabelsMenuObserverTest,
-                       MAYBE_AccessibilityLabelsShowWithScreenReaderEnabled) {
+                       AccessibilityLabelsShowWithScreenReaderEnabled) {
   // Spoof a screen reader.
   content::BrowserAccessibilityState::GetInstance()->AddAccessibilityModeFlags(
       ui::AXMode::kScreenReader);
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
index 483e01c..35a71c8 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
@@ -92,19 +92,13 @@
   cvox.OptionsPage.disableEventStreamFilterCheckBoxes(
       localStorage['enableEventStreamLogging'] == 'false');
 
-  chrome.commandLinePrivate.hasSwitch('enable-audio-focus', function(result) {
-    if (!result) {
-      $('audioStrategy').hidden = true;
-      $('audioDescription').hidden = true;
-    }
-    if (localStorage['audioStrategy']) {
-      for (var i = 0, opt; opt = $('audioStrategy').options[i]; i++) {
-        if (opt.id == localStorage['audioStrategy']) {
-          opt.setAttribute('selected', '');
-        }
+  if (localStorage['audioStrategy']) {
+    for (var i = 0, opt; opt = $('audioStrategy').options[i]; i++) {
+      if (opt.id == localStorage['audioStrategy']) {
+        opt.setAttribute('selected', '');
       }
     }
-  });
+  }
 
   var registerEventStreamFiltersListener = function() {
     $('toggleEventStreamFilters').addEventListener('click', function(evt) {
diff --git a/chrome/browser/resources/local_ntp/doodles.js b/chrome/browser/resources/local_ntp/doodles.js
index 98e6c2b..4aa99b4 100644
--- a/chrome/browser/resources/local_ntp/doodles.js
+++ b/chrome/browser/resources/local_ntp/doodles.js
@@ -313,8 +313,9 @@
 doodles.isDoodleCurrentlyVisible = function() {
   var haveDoodle = ($(doodles.IDS.LOGO_DOODLE)
                         .classList.contains(doodles.CLASSES.SHOW_LOGO));
-  var wantDoodle = (doodles.targetDoodle.image !== null) &&
-      (doodles.targetDoodle.metadata !== null);
+  var wantDoodle = (doodles.targetDoodle.metadata !== null) &&
+      (doodles.targetDoodle.image !== null ||
+       doodles.targetDoodle.metadata.type === doodles.LOGO_TYPE.INTERACTIVE);
   if (!haveDoodle || !wantDoodle) {
     return haveDoodle === wantDoodle;
   }
diff --git a/chrome/browser/resources/local_ntp/icons/add_link_white.svg b/chrome/browser/resources/local_ntp/icons/add_link_white.svg
index 21ccdfb..c491f52 100644
--- a/chrome/browser/resources/local_ntp/icons/add_link_white.svg
+++ b/chrome/browser/resources/local_ntp/icons/add_link_white.svg
@@ -1,6 +1,6 @@
 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
   <g fill="none" fill-rule="evenodd">
-    <rect width="2" height="12" x="7" y="2" fill="#F8F9FA"/>
-    <rect width="2" height="12" x="7" y="2" fill="#F8F9FA" transform="rotate(-90 8 8)"/>
+    <rect width="2" height="12" x="7" y="2" fill="#E8EAED"/>
+    <rect width="2" height="12" x="7" y="2" fill="#E8EAED" transform="rotate(-90 8 8)"/>
   </g>
 </svg>
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index a8678f8f..242f282 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -464,6 +464,7 @@
 
 #mv-single {
   border: none;
+  display: block;
   height: 100%;
   width: 100%;
 }
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index ec409c5..dfb0df76 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -24,6 +24,18 @@
   --md-title-height: 24px;
   --md-title-max-height: 28px;
 
+  /* Material Design colors */
+  --dark-mode-bg-rgb: 50, 54, 57;
+  --GG900-rgb: 32, 33, 36;
+  --GG800-rgb: 60, 64, 67;
+  --GG700-rgb: 95, 99, 104;
+  --GG600-rgb: 128, 134, 139;
+  --GG500-rgb: 154, 160, 166;
+  --GG400-rgb: 189, 193, 198;
+  --GG300-rgb: 218, 220, 224;
+  --GG200-rgb: 232, 234, 237;
+  --GG100-rgb: 241, 243, 244;
+  --GG50-rgb: 248, 249, 250;
 
   /* May be overridden by themes (on the body element). */
   --tile-title-color: #323232;
@@ -100,13 +112,13 @@
 .md-tile-container.reorder .md-tile {
   background-color: white;
   border-radius: 4px;
-  box-shadow: 0 1px 3px 0 rgba(60, 64, 67, 0.3),
-      0 4px 8px 3px rgba(60, 64, 67, 0.15);
+  box-shadow: 0 1px 3px 0 rgba(var(--GG800-rgb), 0.3),
+      0 4px 8px 3px rgba(var(--GG800-rgb), 0.15);
   transition-duration: 200ms;
 }
 
 html[darkmode=true] .md-tile-container.reorder .md-tile {
-  background-color: rgb(50, 54, 57);
+  background-color: rgb(var(--dark-mode-bg-rgb));
   box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4),
       0 4px 8px 3px rgba(0, 0, 0, 0.25);
 }
@@ -126,28 +138,24 @@
 }
 
 body:not(.reordering) .md-tile-container:hover {
-  background-color: rgba(33, 32, 36, 0.06);
+  background-color: rgba(var(--GG900-rgb), 0.06);
 }
 
 html[darkmode=true] body:not(.reordering) .md-tile-container:hover {
-  background-color: rgba(0, 0, 0, 0.1);
+  background-color: rgba(255, 255, 255, 0.1);
 }
 
 body.dark-theme:not(.reordering) .md-tile-container:hover {
   background-color: rgba(255, 255, 255, 0.1);
 }
 
-html[darkmode=true] body.dark-theme:not(.reordering) .md-tile-container:hover {
-  background-color: rgba(218, 220, 224, 0.1);
-}
-
 body:not(.reordering) .md-tile-container:hover .md-menu {
   opacity: 1;
   transition-delay: 500ms;
 }
 
 body.dark-theme:not(.reordering) .md-tile-container:active + .md-menu::after {
-  background-color: rgb(189, 193, 198);
+  background-color: rgb(var(--GG400-rgb));
   transition-delay: 0ms;
 }
 
@@ -183,7 +191,7 @@
 
 .md-icon-background {
   align-items: center;
-  background-color: rgb(241, 243, 244);
+  background-color: rgb(var(--GG100-rgb));
   border-radius: 50%;
   display: flex;
   height: var(--md-icon-size);
@@ -192,7 +200,7 @@
 }
 
 html[darkmode=true] .md-icon-background {
-  background-color: rgb(32, 33, 36);
+  background-color: rgb(var(--GG900-rgb));
 }
 
 .md-add-icon {
@@ -206,7 +214,7 @@
 }
 
 .md-fallback-letter {
-  background-color: rgb(136, 136, 136);
+  background-color: rgb(var(--GG600-rgb));
   border-radius: 50%;
   color: white;
   font-family: 'Segoe UI', 'Roboto', arial, sans-serif;
@@ -218,7 +226,7 @@
 }
 
 .md-title {
-  color: rgba(33, 32, 36, 0.86);
+  color: rgb(var(--GG800-rgb));
   font-family: 'Roboto', arial, sans-serif;
   font-size: var(--md-title-font-size);
   font-weight: 500;
@@ -242,12 +250,12 @@
 
 /* Apply when Chrome is in dark mode. */
 html[darkmode=true] .md-title {
-  color: rgb(248, 249, 250);
+  color: rgb(var(--GG200-rgb));
 }
 
 /* Apply when a custom background is set. */
 body.dark-theme .md-tile-container:not(.reorder) .md-title {
-  color: rgb(248, 249, 250);
+  color: rgb(var(--GG50-rgb));
   filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.35));
 }
 
@@ -260,20 +268,20 @@
 }
 
 html[darkmode=true] body.using-theme .md-title-container {
-  background-color: rgb(32, 33, 36);
+  background-color: rgb(var(--GG900-rgb));
 }
 
 html[darkmode=true] body.using-theme .md-tile-container.reorder .md-title-container {
-  background-color: rgb(50, 54, 57);
+  background-color: rgb(var(--dark-mode-bg-rgb));
 }
 
 body.using-theme .md-tile-container:not(.reorder) .md-title {
-  color: rgba(33, 32, 36, 0.86);
+  color: rgb(var(--GG800-rgb));
   filter: unset;
 }
 
 html[darkmode=true] body.using-theme .md-tile-container:not(.reorder) .md-title {
-  color: rgb(248, 249, 250);
+  color: rgb(var(--GG200-rgb));
 }
 
 .md-menu {
@@ -307,7 +315,7 @@
       url(chrome-search://local-ntp/images/close_3_mask.png@2x) 2x);
   -webkit-mask-repeat: no-repeat;
   -webkit-mask-size: var(--mask-width);
-  background-color: rgba(33, 32, 36, 0.55);
+  background-color: rgb(var(--GG600-rgb));
   content: '';
   display: block;
   height: var(--md-menu-size);
@@ -318,7 +326,7 @@
 }
 
 html[darkmode=true] .md-menu::after {
-  background-color: rgb(248, 249, 250);
+  background-color: rgb(var(--GG200-rgb));
 }
 
 .md-edit-menu {
@@ -335,12 +343,12 @@
 
 body:not(.reordering) .md-menu:hover::after,
 body:not(.reordering) .md-menu:focus::after {
-  background-color: rgba(33, 32, 36, 0.71);
+  background-color: rgb(var(--GG700-rgb));
 }
 
 html[darkmode=true] body:not(.reordering) .md-menu:hover::after,
 html[darkmode=true] body:not(.reordering) .md-menu:focus::after {
-  background-color: rgb(189, 193, 198);
+  background-color: rgb(var(--GG400-rgb));
 }
 
 body.dark-theme .md-tile-container:not(.reorder) .md-menu::after,
@@ -348,8 +356,19 @@
   background-color: white;
 }
 
+html[darkmode=true] body.dark-theme .md-tile-container:not(.reorder) .md-menu::after,
+html[darkmode=true] body.dark-theme:not(.reordering) .md-menu:focus:not(.mouse-navigation)::after {
+  background-color: rgb(var(--GG200-rgb));
+}
+
 body.dark-theme:not(.reordering) .md-menu:active::after,
 body.dark-theme:not(.reordering) .md-menu:hover::after,
 body.dark-theme:not(.reordering) .md-menu.mouse-navigation:focus::after {
-  background-color: rgb(218, 220, 224);
+  background-color: rgb(var(--GG300-rgb));
+}
+
+html[darkmode=true] body.dark-theme:not(.reordering) .md-menu:active::after,
+html[darkmode=true] body.dark-theme:not(.reordering) .md-menu:hover::after,
+html[darkmode=true] body.dark-theme:not(.reordering) .md-menu.mouse-navigation:focus::after {
+  background-color: rgb(var(--GG400-rgb));
 }
diff --git a/chrome/browser/resources/local_ntp/voice.css b/chrome/browser/resources/local_ntp/voice.css
index 8a33a16..4d97344 100644
--- a/chrome/browser/resources/local_ntp/voice.css
+++ b/chrome/browser/resources/local_ntp/voice.css
@@ -41,22 +41,28 @@
 .overlay-dialog {
   background: transparent;
   border: none;
+  height: 100%;
+  left: 0;
+  margin: auto;
+  padding: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 100%;
+}
+
+.overlay-dialog::backdrop {
+  background-color: white;
 }
 
 /* The background element. */
 .overlay,
 .overlay-hidden {
   background: white;
-  height: 100%;
-  left: 0;
   opacity: 0;
-  overflow: hidden;
-  position: fixed;
   text-align: left;
-  top: 0;
   transition: visibility 0s linear 218ms, background-color 218ms;
   visibility: hidden;
-  width: 100%;
   z-index: 10000;
 }
 
@@ -77,10 +83,11 @@
   margin: 15px;
   opacity: .54;
   padding: 0;
-  position: absolute;
+  position: fixed;
   right: 0;
   top: 0;
   width: 15px;
+  z-index: 1;
 }
 
 html[dir=rtl] .close-button {
@@ -157,7 +164,7 @@
   opacity: 0;
   pointer-events: none;
   position: absolute;
-  transition-delay: 0;
+  transition-delay: 0s;
 }
 
 /* Button state when speech recognition is active. */
@@ -165,7 +172,7 @@
   opacity: 1;
   pointer-events: auto;
   position: absolute;
-  transition-delay: 0;
+  transition-delay: 0s;
 }
 
 /* Button state when speech input is being received by the microphone. */
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html
index 0b573697..a6f717e 100644
--- a/chrome/browser/resources/md_downloads/item.html
+++ b/chrome/browser/resources/md_downloads/item.html
@@ -64,7 +64,7 @@
       }
 
       :host-context(html:not([dark])) #content.is-active {
-        background: var(--cr-card-background-color);
+        background-color: var(--cr-card-background-color);
       }
 
       #content:not(.is-active) {
diff --git a/chrome/browser/resources/md_extensions/item.html b/chrome/browser/resources/md_extensions/item.html
index da73f6c..be6dcbf 100644
--- a/chrome/browser/resources/md_extensions/item.html
+++ b/chrome/browser/resources/md_extensions/item.html
@@ -60,7 +60,7 @@
 
       #card {
         @apply --cr-card-elevation;
-        background: var(--cr-card-background-color);
+        background-color: var(--cr-card-background-color);
         border-radius: var(--cr-card-border-radius);
         display: flex;
         flex-direction: column;
diff --git a/chrome/browser/resources/md_history/history_item.html b/chrome/browser/resources/md_history/history_item.html
index 435c072..2a85523e 100644
--- a/chrome/browser/resources/md_history/history_item.html
+++ b/chrome/browser/resources/md_history/history_item.html
@@ -148,7 +148,7 @@
 
       #background {
         @apply --cr-card-elevation;
-        background: var(--cr-card-background-color);
+        background-color: var(--cr-card-background-color);
         bottom: 0;
         left: 0;
         position: absolute;
diff --git a/chrome/browser/resources/md_history/synced_device_card.html b/chrome/browser/resources/md_history/synced_device_card.html
index 7a9b5a14..9bed5de4 100644
--- a/chrome/browser/resources/md_history/synced_device_card.html
+++ b/chrome/browser/resources/md_history/synced_device_card.html
@@ -58,7 +58,7 @@
 
       #history-item-container {
         @apply --cr-card-elevation;
-        background: var(--cr-card-background-color);
+        background-color: var(--cr-card-background-color);
         border-radius: var(--cr-card-border-radius);
       }
 
diff --git a/chrome/browser/resources/omnibox/omnibox_input.js b/chrome/browser/resources/omnibox/omnibox_input.js
index 39b6b56..72b1063 100644
--- a/chrome/browser/resources/omnibox/omnibox_input.js
+++ b/chrome/browser/resources/omnibox/omnibox_input.js
@@ -32,6 +32,87 @@
     this.displayInputs = OmniboxInput.defaultDisplayInputs;
   }
 
+  /** @override */
+  connectedCallback() {
+    this.setupElementListeners_();
+  }
+
+  /** @private */
+  setupElementListeners_() {
+    ['#input-text',
+     '#reset-autocomplete-controller',
+     '#lock-cursor-position',
+     '#zero-suggest',
+     '#prevent-inline-autocomplete',
+     '#prefer-keyword',
+     '#current-url',
+     '#page-classification',
+    ]
+        .forEach(
+            query => this.$$(query).addEventListener(
+                'input', this.onQueryInputsChanged_.bind(this)));
+
+    // Set text of .arrow-padding to substring of #input-text text, from
+    // beginning until cursor position, in order to correctly align .arrow-up.
+    this.$$('#input-text')
+        .addEventListener(
+            'input', this.positionCursorPositionIndicators_.bind(this));
+
+    this.$$('#response-selection')
+        .addEventListener('input', this.onResponseSelectionChanged_.bind(this));
+    this.$$('#response-selection')
+        .addEventListener('blur', this.onResponseSelectionBlur_.bind(this));
+
+    ['#show-incomplete-results',
+     '#show-details',
+     '#show-all-providers',
+    ]
+        .forEach(
+            query => this.$$(query).addEventListener(
+                'input', this.onDisplayInputsChanged_.bind(this)));
+
+    this.$$('#filter-text')
+        .addEventListener('input', this.onFilterInputsChanged_.bind(this));
+
+    this.$$('#copy-text')
+        .addEventListener('click', this.onCopyText_.bind(this));
+    this.$$('#download-json')
+        .addEventListener('click', this.onDownloadJson_.bind(this));
+    this.setupDragListeners_(this.$$('#import-json'));
+    this.$$('#import-json')
+        .addEventListener('drop', this.onImportDropped_.bind(this));
+    this.$$('#import-json-input')
+        .addEventListener('input', this.onImportFileSelected_.bind(this));
+  }
+
+  /**
+   * Sets up boilerplate event listeners for an element that is able to receive
+   * drag events.
+   * @private @param {!Element} element
+   */
+  setupDragListeners_(element) {
+    element.addEventListener(
+        'dragenter', () => element.classList.add('drag-hover'));
+    element.addEventListener(
+        'dragleave', () => element.classList.remove('drag-hover'));
+    element.addEventListener('dragover', e => e.preventDefault());
+    element.addEventListener('drop', e => {
+      e.preventDefault();
+      element.classList.remove('drag-hover');
+    });
+  }
+
+  /** @private */
+  onQueryInputsChanged_() {
+    this.$$('#imported-warning').hidden = true;
+    this.$$('#current-url').disabled = this.$$('#zero-suggest').checked;
+    if (this.$$('#zero-suggest').checked) {
+      this.$$('#current-url').value = this.$$('#input-text').value;
+    }
+    this.dispatchEvent(
+        new CustomEvent('query-inputs-changed', {detail: this.queryInputs}));
+  }
+
   /** @return {QueryInputs} */
   get queryInputs() {
     return {
@@ -64,6 +145,62 @@
     this.$$('#page-classification').value = queryInputs.pageClassification;
   }
 
+  /** @private @return {number} */
+  get cursorPosition_() {
+    return this.$$('#lock-cursor-position').checked ?
+        this.$$('#input-text').value.length :
+        this.$$('#input-text').selectionEnd;
+  }
+
+  /** @private @param {number} value */
+  set cursorPosition_(value) {
+    this.$$('#input-text').setSelectionRange(value, value);
+    this.positionCursorPositionIndicators_();
+  }
+
+  /** @private */
+  positionCursorPositionIndicators_() {
+    this.$$('.arrow-padding').textContent =
+        this.$$('#input-text').value.substring(0, this.cursorPosition_);
+  }
+
+  /** @return {boolean} */
+  get connectWindowOmnibox() {
+    return this.$$('#connect-window-omnibox').checked;
+  }
+
+  /** @private */
+  onResponseSelectionChanged_() {
+    const {value, max} = this.$$('#response-selection');
+    this.$$('#history-warning').hidden = value === '0' || value === max;
+    this.dispatchEvent(new CustomEvent('response-select', {detail: value - 1}));
+  }
+
+  /** @private */
+  onResponseSelectionBlur_() {
+    const {value, min, max} = this.$$('#response-selection');
+    this.$$('#response-selection').value = Math.max(Math.min(value, max), min);
+    this.onResponseSelectionChanged_();
+  }
+
+  /** @param {number} value */
+  set responsesCount(value) {
+    if (this.$$('#response-selection').value ===
+        this.$$('#response-selection').max) {
+      this.$$('#response-selection').value = value;
+    }
+    this.$$('#response-selection').max = value;
+    this.$$('#response-selection').min = value ? 1 : 0;
+    this.$$('#responses-count').textContent = value;
+    this.onResponseSelectionBlur_();
+  }
+
+  /** @private */
+  onDisplayInputsChanged_() {
+    this.dispatchEvent(new CustomEvent(
+        'display-inputs-changed', {detail: this.displayInputs}));
+  }
+
   /** @return {DisplayInputs} */
   get displayInputs() {
     return {
@@ -81,93 +218,6 @@
     this.$$('#show-all-providers').checked = displayInputs.showAllProviders;
   }
 
-  /** @override */
-  connectedCallback() {
-    this.setupElementListeners_();
-  }
-
-  /** @private */
-  setupElementListeners_() {
-    ['#input-text',
-     '#reset-autocomplete-controller',
-     '#lock-cursor-position',
-     '#zero-suggest',
-     '#prevent-inline-autocomplete',
-     '#prefer-keyword',
-     '#current-url',
-     '#page-classification',
-    ]
-        .forEach(
-            query => this.$$(query).addEventListener(
-                'input', this.onQueryInputsChanged_.bind(this)));
-
-    ['#show-incomplete-results',
-     '#show-details',
-     '#show-all-providers',
-    ]
-        .forEach(
-            query => this.$$(query).addEventListener(
-                'input', this.onDisplayInputsChanged_.bind(this)));
-
-    this.$$('#filter-text')
-        .addEventListener('input', this.onFilterInputsChanged_.bind(this));
-
-    this.$$('#copy-text')
-        .addEventListener('click', this.onCopyText_.bind(this));
-    this.$$('#download-json')
-        .addEventListener('click', this.onDownloadJson_.bind(this));
-    this.setupDragListeners_(this.$$('#import-json'));
-    this.$$('#import-json')
-        .addEventListener('drop', this.onImportDropped_.bind(this));
-    this.$$('#import-json-input')
-        .addEventListener('input', this.onImportFileSelected_.bind(this));
-
-    // Set text of .arrow-padding to substring of #input-text text, from
-    // beginning until cursor position, in order to correctly align .arrow-up.
-    this.$$('#input-text')
-        .addEventListener(
-            'input', this.positionCursorPositionIndicators_.bind(this));
-
-    this.$$('#response-selection')
-        .addEventListener('input', this.onResponseSelectionChanged_.bind(this));
-    this.$$('#response-selection')
-        .addEventListener('blur', this.onResponseSelectionBlur_.bind(this));
-  }
-
-  /**
-   * Sets up boilerplate event listeners for an element that is able to receive
-   * drag events.
-   * @private @param {!Element} element
-   */
-  setupDragListeners_(element) {
-    element.addEventListener(
-        'dragenter', () => element.classList.add('drag-hover'));
-    element.addEventListener(
-        'dragleave', () => element.classList.remove('drag-hover'));
-    element.addEventListener('dragover', e => e.preventDefault());
-    element.addEventListener('drop', e => {
-      e.preventDefault();
-      element.classList.remove('drag-hover');
-    });
-  }
-
-  /** @private */
-  onQueryInputsChanged_() {
-    this.$$('#imported-warning').hidden = true;
-    this.$$('#current-url').disabled = this.$$('#zero-suggest').checked;
-    if (this.$$('#zero-suggest').checked) {
-      this.$$('#current-url').value = this.$$('#input-text').value;
-    }
-    this.dispatchEvent(
-        new CustomEvent('query-inputs-changed', {detail: this.queryInputs}));
-  }
-
-  /** @private */
-  onDisplayInputsChanged_() {
-    this.dispatchEvent(new CustomEvent(
-        'display-inputs-changed', {detail: this.displayInputs}));
-  }
-
   /** @private */
   onFilterInputsChanged_() {
     this.dispatchEvent(new CustomEvent(
@@ -223,56 +273,6 @@
     }
   }
 
-  /** @private */
-  onResponseSelectionChanged_() {
-    const {value, max} = this.$$('#response-selection');
-    this.$$('#history-warning').hidden = value === '0' || value === max;
-    this.dispatchEvent(new CustomEvent('response-select', {detail: value - 1}));
-  }
-
-  /** @private */
-  onResponseSelectionBlur_() {
-    const {value, min, max} = this.$$('#response-selection');
-    this.$$('#response-selection').value = Math.max(Math.min(value, max), min);
-    this.onResponseSelectionChanged_();
-  }
-
-  /** @param {number} value */
-  set responsesCount(value) {
-    if (this.$$('#response-selection').value ===
-        this.$$('#response-selection').max) {
-      this.$$('#response-selection').value = value;
-    }
-    this.$$('#response-selection').max = value;
-    this.$$('#response-selection').min = value ? 1 : 0;
-    this.$$('#responses-count').textContent = value;
-    this.onResponseSelectionBlur_();
-  }
-
-  /** @return {boolean} */
-  get connectWindowOmnibox() {
-    return this.$$('#connect-window-omnibox').checked;
-  }
-
-  /** @private @return {number} */
-  get cursorPosition_() {
-    return this.$$('#lock-cursor-position').checked ?
-        this.$$('#input-text').value.length :
-        this.$$('#input-text').selectionEnd;
-  }
-
-  /** @private @param {number} value */
-  set cursorPosition_(value) {
-    this.$$('#input-text').setSelectionRange(value, value);
-    this.positionCursorPositionIndicators_();
-  }
-
-  /** @private */
-  positionCursorPositionIndicators_() {
-    this.$$('.arrow-padding').textContent =
-        this.$$('#input-text').value.substring(0, this.cursorPosition_);
-  }
-
   /** @return {DisplayInputs} */
   static get defaultDisplayInputs() {
     return {
diff --git a/chrome/browser/resources/print_preview/new/BUILD.gn b/chrome/browser/resources/print_preview/new/BUILD.gn
index ee10ae8c..807d0af7 100644
--- a/chrome/browser/resources/print_preview/new/BUILD.gn
+++ b/chrome/browser/resources/print_preview/new/BUILD.gn
@@ -108,6 +108,7 @@
 
 js_library("destination_select") {
   deps = [
+    ":select_behavior",
     "../data:destination",
     "../data:user_info",
     "//ui/webui/resources/js:i18n_behavior",
diff --git a/chrome/browser/resources/print_preview/new/destination_select.html b/chrome/browser/resources/print_preview/new/destination_select.html
index 94d5b39..dcdbafc 100644
--- a/chrome/browser/resources/print_preview/new/destination_select.html
+++ b/chrome/browser/resources/print_preview/new/destination_select.html
@@ -11,6 +11,7 @@
 <link rel="import" href="../data/destination.html">
 <link rel="import" href="../data/user_info.html">
 <link rel="import" href="print_preview_shared_css.html">
+<link rel="import" href="select_behavior.html">
 <link rel="import" href="strings.html">
 
 <dom-module id="print-preview-destination-select">
@@ -18,6 +19,7 @@
     <style include="print-preview-shared md-select cr-hidden-style">
       .md-select {
         padding-inline-start: 32px;
+        --md-select-width: calc(100% - 4px);
       }
     </style>
     <select class="md-select" aria-label$="[[i18n(destinationLabel)]]"
@@ -28,7 +30,7 @@
             calc(100% - 8px) center/10px no-repeat;
             background-color: var(--google-grey-100);"
         disabled$="[[disabled]]"
-        on-change="onSelectedDestinationOptionChange_">
+        value="{{selectedValue::change}}">
       <option value="noDestinations" hidden$="[[!noDestinationsFound]]"
           selected$="[[noDestinationsFound]]">
         $i18n{noDestinationsMessage}
@@ -36,8 +38,7 @@
       <template is="dom-repeat" items="[[recentDestinationList]]">
         <option value="[[item.key]]">[[item.displayName]]</option>
       </template>
-      <option value="[[getPdfDestinationKey_()]]"
-          hidden$="[[!showSaveAsPdf_]]">
+      <option value="[[getPdfDestinationKey_()]]" hidden$="[[appKioskMode]]">
         $i18n{printToPDF}
       </option>
       <option value="[[getGoogleDriveDestinationKey_(activeUser)]]"
diff --git a/chrome/browser/resources/print_preview/new/destination_select.js b/chrome/browser/resources/print_preview/new/destination_select.js
index 19b4c57..5e02797e 100644
--- a/chrome/browser/resources/print_preview/new/destination_select.js
+++ b/chrome/browser/resources/print_preview/new/destination_select.js
@@ -5,7 +5,7 @@
 Polymer({
   is: 'print-preview-destination-select',
 
-  behaviors: [I18nBehavior],
+  behaviors: [I18nBehavior, print_preview_new.SelectBehavior],
 
   properties: {
     activeUser: String,
@@ -26,38 +26,21 @@
     recentDestinationList: Array,
 
     /** @private {boolean} */
-    showGoogleDrive_: {
-      type: Boolean,
-      computed: 'computeShowGoogleDrive_(' +
-          'recentDestinationList.*, cloudPrintState)',
-    },
-
-    /** @private {boolean} */
-    showSaveAsPdf_: {
-      type: Boolean,
-      computed: 'computeShowSaveAsPdf_(recentDestinationList.*, appKioskMode)',
-    },
+    showGoogleDrive_:
+        {type: Boolean, computed: 'computeShowGoogleDrive_(cloudPrintState)'},
   },
 
   /** @private {!IronMetaElement} */
   meta_: /** @type {!IronMetaElement} */ (
       Polymer.Base.create('iron-meta', {type: 'iconset'})),
 
-  /** Sets the select to the current value of |destination|. */
-  updateDestination: function() {
-    this.$$('.md-select').value = this.destination.key;
+  focus: function() {
+    this.$$('.md-select').focus();
   },
 
-  /**
-   * @return {boolean} Whether to show the Google Drive option.
-   * @private
-   */
-  computeShowSaveAsPdf_: function() {
-    return !this.appKioskMode && this.recentDestinationList &&
-        !this.recentDestinationList.some(destination => {
-          return destination.id ===
-              print_preview.Destination.GooglePromotedId.SAVE_AS_PDF;
-        });
+  /** Sets the select to the current value of |destination|. */
+  updateDestination: function() {
+    this.selectedValue = this.destination.key;
   },
 
   /**
@@ -65,11 +48,7 @@
    * @private
    */
   computeShowGoogleDrive_: function() {
-    return this.cloudPrintState === print_preview.CloudPrintState.SIGNED_IN &&
-        this.recentDestinationList &&
-        !this.recentDestinationList.some(
-            destination => destination.id ===
-                print_preview.Destination.GooglePromotedId.DOCS);
+    return this.cloudPrintState === print_preview.CloudPrintState.SIGNED_IN;
   },
 
   /**
@@ -115,7 +94,7 @@
   },
 
   /** @private */
-  onSelectedDestinationOptionChange_: function() {
-    this.fire('selected-option-change', this.$$('.md-select').value);
+  onProcessSelectChange: function(value) {
+    this.fire('selected-option-change', value);
   },
 });
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.html b/chrome/browser/resources/print_preview/new/destination_settings.html
index 6b50a14..4bdf89f 100644
--- a/chrome/browser/resources/print_preview/new/destination_settings.html
+++ b/chrome/browser/resources/print_preview/new/destination_settings.html
@@ -35,6 +35,10 @@
         white-space: nowrap;
       }
 
+      #destinationSelect {
+        margin-inline-start: 13px;
+      }
+
       print-preview-settings-section.destination-status-wrapper,
       print-preview-settings-section.destination-status-wrapper div {
         height: 100%;
@@ -58,7 +62,8 @@
             cloud-print-state="[[cloudPrintState]]"
             destination="[[destination]]"
             disabled="[[shouldDisableDropdown_(destinationStore, disabled,
-                                               state, noDestinationsFound)]]"
+                                               loadingDestination_, state,
+                                               noDestinationsFound)]]"
             no-destinations-found="[[noDestinationsFound]]"
             recent-destination-list="[[recentDestinationList_]]"
             on-selected-option-change="onSelectedDestinationOptionChange_">
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.js b/chrome/browser/resources/print_preview/new/destination_settings.js
index 7e40adf..97a102aa 100644
--- a/chrome/browser/resources/print_preview/new/destination_settings.js
+++ b/chrome/browser/resources/print_preview/new/destination_settings.js
@@ -50,6 +50,7 @@
     loadingDestination_: {
       type: Boolean,
       value: true,
+      observer: 'onLoadingDestinationChange_',
     },
 
     /** @private {!Array<!print_preview.Destination>} */
@@ -88,6 +89,16 @@
         this.updateRecentDestinationList_.bind(this));
   },
 
+  /**
+   * @param {!print_preview.RecentDestination} destination
+   * @return {boolean} Whether the destination is Save as PDF or Save to Drive.
+   */
+  destinationIsDriveOrPdf_: function(destination) {
+    return destination.id ===
+        print_preview.Destination.GooglePromotedId.SAVE_AS_PDF ||
+        destination.id === print_preview.Destination.GooglePromotedId.DOCS;
+  },
+
   /** @private */
   updateRecentDestinationList_: function() {
     if (!this.recentDestinations || !this.destinationStore) {
@@ -95,32 +106,34 @@
     }
 
     const recentDestinations = [];
+    let update = false;
     let filterAccount = this.activeUser;
     // Fallback to the account for the current destination, in case activeUser
     // is not known yet from cloudprint.
     if (!filterAccount) {
       filterAccount = this.destination ? this.destination.account : '';
     }
+    const existingKeys = this.recentDestinationList_ ?
+        this.recentDestinationList_.map(listItem => listItem.key) :
+        [];
     this.recentDestinations.forEach(recentDestination => {
-      const destination = this.destinationStore.getDestinationByKey(
-          print_preview.createRecentDestinationKey(recentDestination));
-      if (destination &&
+      const key = print_preview.createRecentDestinationKey(recentDestination);
+      const destination = this.destinationStore.getDestinationByKey(key);
+      if (destination && !this.destinationIsDriveOrPdf_(recentDestination) &&
           (!destination.account || destination.account == filterAccount)) {
         recentDestinations.push(destination);
+        update = update || !existingKeys.includes(key);
       }
     });
-    this.recentDestinationList_ = recentDestinations;
-    // Update the select value only after re-rendering the dropdown items.
-    // Otherwise, the select will sometimes display the wrong printer value
-    // (even though |destination| is correctly updated). Note that changing
-    // |destination| will always result in a subsequent change to
-    // |recentDestinations|.
-    Polymer.RenderStatus.afterNextRender(this, () => {
-      this.loadingDestination_ = !this.destination || !this.destination.id;
-      if (!this.loadingDestination_) {
-        this.$.destinationSelect.updateDestination();
-      }
-    });
+
+    // Only update the list if new destinations have been added to it.
+    // Re-ordering the dropdown items every time the selected item changes is
+    // a bad experience for keyboard users.
+    if (update) {
+      this.recentDestinationList_ = recentDestinations;
+    }
+
+    this.loadingDestination_ = !this.destination || !this.destination.id;
   },
 
   /**
@@ -129,7 +142,8 @@
    */
   shouldDisableDropdown_: function() {
     return !this.destinationStore || this.noDestinationsFound ||
-        (this.disabled &&
+        this.loadingDestination_ ||
+        (this.disabled && this.state != print_preview_new.State.NOT_READY &&
          this.state != print_preview_new.State.INVALID_PRINTER);
   },
 
@@ -216,4 +230,23 @@
     }
     this.$.destinationSelect.focus();
   },
+
+  /** @private */
+  onLoadingDestinationChange_: function() {
+    if (this.loadingDestination_) {
+      return;
+    }
+
+    // TODO (rbpotter): Remove this conditional when the Polymer 2 migration
+    // is completed.
+    if (Polymer.DomIf) {
+      Polymer.RenderStatus.beforeNextRender(this.$.destinationSelect, () => {
+        this.$.destinationSelect.updateDestination();
+      });
+    } else {
+      this.$.destinationSelect.async(() => {
+        this.$.destinationSelect.updateDestination();
+      });
+    }
+  },
 });
diff --git a/chrome/browser/resources/safe_browsing/download_file_types.asciipb b/chrome/browser/resources/safe_browsing/download_file_types.asciipb
index 760a573..e82e6166 100644
--- a/chrome/browser/resources/safe_browsing/download_file_types.asciipb
+++ b/chrome/browser/resources/safe_browsing/download_file_types.asciipb
@@ -8,7 +8,7 @@
 ##
 ## Top level settings
 ##
-version_id: 26
+version_id: 27
 sampled_ping_probability: 0.01
 max_archived_binaries_to_report: 10
 default_file_type {
@@ -492,6 +492,9 @@
   is_archive: true
   ping_setting: FULL_PING
   inspection_type: RAR
+  platform_settings {
+    max_file_size_to_analyze: 52428800 # 50MB
+  }
 }
 file_types {
   extension: "squashfs"
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
index 444fd075..683d4f2 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
@@ -275,29 +275,14 @@
 
   // Directly use 'dmg' extension since download file may not have any
   // extension, but has still been deemed a DMG through file type sniffing.
-  base::File file(tmp_path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
-  if (!file.IsValid()) {
-    std::move(callback_).Run(std::move(results_));
-    return;
-  }
-  int64_t size = file.GetLength();
-
-  bool too_big_to_unpack =
-      base::checked_cast<uint64_t>(size) >
-      FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze("dmg");
-  UMA_HISTOGRAM_BOOLEAN("SBClientDownload.DmgTooBigToUnpack",
-                        too_big_to_unpack);
-  if (too_big_to_unpack) {
-    std::move(callback_).Run(std::move(results_));
-  } else {
-    dmg_analyzer_ = new SandboxedDMGAnalyzer(
-        tmp_path_,
-        base::BindRepeating(&FileAnalyzer::OnDmgAnalysisFinished,
-                            weakptr_factory_.GetWeakPtr()),
-        content::ServiceManagerConnection::GetForProcess()->GetConnector());
-    dmg_analyzer_->Start();
-    dmg_analysis_start_time_ = base::TimeTicks::Now();
-  }
+  dmg_analyzer_ = new SandboxedDMGAnalyzer(
+      tmp_path_,
+      FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze("dmg"),
+      base::BindRepeating(&FileAnalyzer::OnDmgAnalysisFinished,
+                          weakptr_factory_.GetWeakPtr()),
+      content::ServiceManagerConnection::GetForProcess()->GetConnector());
+  dmg_analyzer_->Start();
+  dmg_analysis_start_time_ = base::TimeTicks::Now();
 }
 
 void FileAnalyzer::ExtractFileOrDmgFeatures(
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
index d88d7a8c3..8bcdd4e315 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
@@ -9,10 +9,12 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/safe_browsing/file_type_policies_test_util.h"
 #include "chrome/common/safe_browsing/mock_binary_feature_extractor.h"
+#include "components/safe_browsing/features.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -43,6 +45,8 @@
   void SetUp() override {
     has_result_ = false;
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    scoped_feature_list_.InitAndEnableFeature(
+        safe_browsing::kInspectRarContentFeature);
   }
 
   void TearDown() override {}
@@ -55,6 +59,7 @@
  private:
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
   content::InProcessUtilityThreadHelper in_process_utility_thread_helper_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(FileAnalyzerTest, TypeWinExecutable) {
@@ -694,4 +699,72 @@
 
 #endif
 
+TEST_F(FileAnalyzerTest, SmallRarHasContentInspection) {
+  scoped_refptr<MockBinaryFeatureExtractor> extractor =
+      new testing::StrictMock<MockBinaryFeatureExtractor>();
+  FileAnalyzer analyzer(extractor);
+  base::RunLoop run_loop;
+
+  base::FilePath target_path(FILE_PATH_LITERAL("has_exe.rar"));
+  base::FilePath rar_path;
+  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &rar_path));
+  rar_path = rar_path.AppendASCII("safe_browsing")
+                 .AppendASCII("rar")
+                 .AppendASCII("has_exe.rar");
+
+  // Analyze the RAR with default size limit
+  analyzer.Start(
+      target_path, rar_path,
+      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
+                     run_loop.QuitClosure()));
+  run_loop.Run();
+
+  ASSERT_TRUE(has_result_);
+  EXPECT_EQ(result_.type, ClientDownloadRequest::RAR_COMPRESSED_EXECUTABLE);
+  EXPECT_EQ(result_.archive_is_valid, FileAnalyzer::ArchiveValid::VALID);
+  ASSERT_EQ(1, result_.archived_binaries.size());
+
+  // Since the file is small enough, we should have a sha256
+  EXPECT_FALSE(result_.archived_binaries.Get(0).digests().sha256().empty());
+}
+
+TEST_F(FileAnalyzerTest, LargeRarSkipsContentInspection) {
+  scoped_refptr<MockBinaryFeatureExtractor> extractor =
+      new testing::StrictMock<MockBinaryFeatureExtractor>();
+  FileAnalyzer analyzer(extractor);
+  base::RunLoop run_loop;
+
+  FileTypePoliciesTestOverlay overlay;
+  std::unique_ptr<DownloadFileTypeConfig> config = overlay.DuplicateConfig();
+  for (DownloadFileType& file_type : *config->mutable_file_types()) {
+    if (file_type.extension() == "rar") {
+      // All archives will skip content inspection.
+      file_type.mutable_platform_settings(0)->set_max_file_size_to_analyze(0);
+      break;
+    }
+  }
+  overlay.SwapConfig(config);
+
+  base::FilePath target_path(FILE_PATH_LITERAL("has_exe.rar"));
+  base::FilePath rar_path;
+  EXPECT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &rar_path));
+  rar_path = rar_path.AppendASCII("safe_browsing")
+                 .AppendASCII("rar")
+                 .AppendASCII("has_exe.rar");
+
+  analyzer.Start(
+      target_path, rar_path,
+      base::BindOnce(&FileAnalyzerTest::DoneCallback, base::Unretained(this),
+                     run_loop.QuitClosure()));
+  run_loop.Run();
+
+  ASSERT_TRUE(has_result_);
+  EXPECT_EQ(result_.type, ClientDownloadRequest::RAR_COMPRESSED_EXECUTABLE);
+  EXPECT_EQ(result_.archive_is_valid, FileAnalyzer::ArchiveValid::VALID);
+  ASSERT_EQ(1, result_.archived_binaries.size());
+
+  // Since the file is too large enough, we should not have any hashes
+  EXPECT_TRUE(result_.archived_binaries.Get(0).digests().sha256().empty());
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc
index 055004c9..366724d 100644
--- a/chrome/browser/search/local_ntp_source.cc
+++ b/chrome/browser/search/local_ntp_source.cc
@@ -420,6 +420,9 @@
 }
 
 std::string ConvertLogoImageToBase64(const EncodedLogo& logo) {
+  if (!logo.encoded_image)
+    return std::string();
+
   std::string base64;
   base::Base64Encode(logo.encoded_image->data(), &base64);
   return base::StringPrintf("data:%s;base64,%s",
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index 0e1ba3fb..f8766cc 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/language/language_model_manager_factory.h"
+#include "chrome/browser/language/url_language_histogram_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/browser/translate/translate_accept_languages_factory.h"
@@ -115,7 +116,9 @@
 
 ChromeTranslateClient::ChromeTranslateClient(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
-      translate_driver_(&web_contents->GetController()),
+      translate_driver_(&web_contents->GetController(),
+                        UrlLanguageHistogramFactory::GetForBrowserContext(
+                            web_contents->GetBrowserContext())),
       translate_manager_(new translate::TranslateManager(
           this,
           translate::TranslateRankerFactory::GetForBrowserContext(
diff --git a/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc
index e4e73ed8..71365db 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_android_unittest.cc
@@ -58,7 +58,8 @@
     details.adopted_language = lang;
     ChromeTranslateClient::FromWebContents(web_contents())
         ->translate_driver()
-        .OnPageReady(fake_page_.BindToNewPagePtr(), details, page_translatable);
+        .RegisterPage(fake_page_.BindToNewPagePtr(), details,
+                      page_translatable);
   }
 
   InfoBarService* infobar_service() {
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index 801c8f8..2dd2215 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -252,7 +252,8 @@
     details.adopted_language = lang;
     ChromeTranslateClient::FromWebContents(web_contents())
         ->translate_driver()
-        .OnPageReady(fake_page_.BindToNewPagePtr(), details, page_translatable);
+        .RegisterPage(fake_page_.BindToNewPagePtr(), details,
+                      page_translatable);
   }
 
   void SimulateOnPageTranslated(const std::string& source_lang,
diff --git a/chrome/browser/ui/blocked_content/list_item_position.h b/chrome/browser/ui/blocked_content/list_item_position.h
index 19cacb8..2efb252 100644
--- a/chrome/browser/ui/blocked_content/list_item_position.h
+++ b/chrome/browser/ui/blocked_content/list_item_position.h
@@ -14,13 +14,13 @@
 // framebust and popup UI on desktop platforms to indicate which element was
 // clicked.
 enum class ListItemPosition : int {
-  kOnlyItem,
-  kFirstItem,
-  kMiddleItem,
-  kLastItem,
+  kOnlyItem = 0,
+  kFirstItem = 1,
+  kMiddleItem = 2,
+  kLastItem = 3,
 
   // Any new values should go before this one.
-  kLast,
+  kMaxValue = kLastItem,
 };
 
 // Gets the list item position from the given distance/index and the total size
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
index 432f629..dcc5740 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
@@ -113,7 +113,7 @@
       std::distance(blocked_popups_.begin(), it), blocked_popups_.size());
 
   UMA_HISTOGRAM_ENUMERATION("ContentSettings.Popups.ClickThroughPosition",
-                            position, ListItemPosition::kLast);
+                            position);
 
   BlockedRequest* popup = it->second.get();
 
diff --git a/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.cc b/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.cc
index e345ae9..7a5289b 100644
--- a/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.cc
+++ b/chrome/browser/ui/blocked_content/tab_under_navigation_throttle.cc
@@ -65,17 +65,19 @@
 #if defined(OS_ANDROID)
 typedef FramebustBlockMessageDelegate::InterventionOutcome InterventionOutcome;
 
-void LogOutcome(bool off_the_record, InterventionOutcome outcome) {
-  TabUnderNavigationThrottle::Action action;
+TabUnderNavigationThrottle::Action GetActionForOutcome(
+    InterventionOutcome outcome) {
   switch (outcome) {
     case InterventionOutcome::kAccepted:
-      action = TabUnderNavigationThrottle::Action::kAcceptedIntervention;
-      break;
+      return TabUnderNavigationThrottle::Action::kAcceptedIntervention;
     case InterventionOutcome::kDeclinedAndNavigated:
-      action = TabUnderNavigationThrottle::Action::kClickedThrough;
-      break;
+      return TabUnderNavigationThrottle::Action::kClickedThrough;
   }
-  LogAction(action, off_the_record);
+  NOTREACHED();
+}
+
+void LogOutcome(bool off_the_record, InterventionOutcome outcome) {
+  LogAction(GetActionForOutcome(outcome), off_the_record);
 }
 #else
 void OnListItemClicked(bool off_the_record,
@@ -85,8 +87,7 @@
   LogAction(TabUnderNavigationThrottle::Action::kClickedThrough,
             off_the_record);
   UMA_HISTOGRAM_ENUMERATION("Tab.TabUnder.ClickThroughPosition",
-                            GetListItemPositionFromDistance(index, total_size),
-                            ListItemPosition::kLast);
+                            GetListItemPositionFromDistance(index, total_size));
 }
 #endif
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 19d7970..994adf6a 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -95,7 +95,9 @@
 #include "chrome/browser/translate/chrome_translate_client.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
+#include "chrome/browser/ui/blocked_content/list_item_position.h"
 #include "chrome/browser/ui/blocked_content/popup_blocker.h"
+#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 #include "chrome/browser/ui/blocked_content/popup_tracker.h"
 #include "chrome/browser/ui/bluetooth/bluetooth_chooser_controller.h"
 #include "chrome/browser/ui/bluetooth/bluetooth_chooser_desktop.h"
@@ -1240,10 +1242,12 @@
                                   const GURL& url) {
   if (auto* framebust_helper =
           FramebustBlockTabHelper::FromWebContents(web_contents)) {
-    // TODO(csharrison): Add a click callback here to collect framebusting
-    // click-through metrics.
-    framebust_helper->AddBlockedUrl(url,
-                                    FramebustBlockTabHelper::ClickCallback());
+    auto on_click = [](const GURL& url, size_t index, size_t total_elements) {
+      UMA_HISTOGRAM_ENUMERATION(
+          "WebCore.Framebust.ClickThroughPosition",
+          GetListItemPositionFromDistance(index, total_elements));
+    };
+    framebust_helper->AddBlockedUrl(url, base::BindOnce(on_click));
   }
 }
 
diff --git a/chrome/browser/ui/interventions/framebust_block_message_delegate.h b/chrome/browser/ui/interventions/framebust_block_message_delegate.h
index dce1f4d..decf30d 100644
--- a/chrome/browser/ui/interventions/framebust_block_message_delegate.h
+++ b/chrome/browser/ui/interventions/framebust_block_message_delegate.h
@@ -23,7 +23,13 @@
   // Describes the actions the user can take regarding this intervention, they
   // are provided through a callback the caller can pass to the delegate's
   // constructor.
-  enum class InterventionOutcome { kAccepted, kDeclinedAndNavigated };
+  // This enum backs a histogram. Any updates should be reflected in enums.xml,
+  // and new elements should only be appended to the end.
+  enum class InterventionOutcome {
+    kAccepted = 0,
+    kDeclinedAndNavigated = 1,
+    kMaxValue = kDeclinedAndNavigated
+  };
 
   typedef base::OnceCallback<void(InterventionOutcome)> OutcomeCallback;
 
diff --git a/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc b/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc
index 25af392..5e60a010 100644
--- a/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc
@@ -395,6 +395,42 @@
 }
 
 IN_PROC_BROWSER_TEST_F(LocalNTPDoodleTest,
+                       ShouldShowInteractiveLogoWithoutImage) {
+  EncodedLogo cached_logo;
+  cached_logo.encoded_image = nullptr;
+  cached_logo.metadata.type = LogoType::INTERACTIVE;
+  cached_logo.metadata.full_page_url =
+      GURL("https://www.chromium.org/interactive");
+  cached_logo.metadata.alt_text = "alt text";
+  cached_logo.metadata.iframe_width_px = 500;
+  cached_logo.metadata.iframe_height_px = 200;
+
+  EXPECT_CALL(*logo_service(), GetLogoPtr(_))
+      .WillRepeatedly(DoAll(
+          ReturnCachedLogo(LogoCallbackReason::DETERMINED, cached_logo),
+          ReturnFreshLogo(LogoCallbackReason::REVALIDATED, base::nullopt)));
+
+  // Open a new blank tab, then go to NTP.
+  content::WebContents* active_tab =
+      local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank"));
+  base::HistogramTester histograms;
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL));
+
+  EXPECT_THAT(GetDimension(active_tab, "fakebox", "top"), Eq(kFakeboxTopPx));
+  EXPECT_THAT(GetComputedOpacity(active_tab, "logo-default"), Eq(0.0));
+  EXPECT_THAT(GetComputedOpacity(active_tab, "logo-doodle"), Eq(1.0));
+  EXPECT_THAT(GetComputedDisplay(active_tab, "logo-doodle-container"),
+              Eq<std::string>("none"));
+  EXPECT_THAT(GetComputedDisplay(active_tab, "logo-doodle-iframe"),
+              Eq<std::string>("block"));
+
+  EXPECT_THAT(GetElementProperty(active_tab, "logo-doodle-iframe", "src"),
+              Eq<std::string>("https://www.chromium.org/interactive"));
+  EXPECT_THAT(GetElementProperty(active_tab, "logo-doodle-iframe", "title"),
+              Eq<std::string>("alt text"));
+}
+
+IN_PROC_BROWSER_TEST_F(LocalNTPDoodleTest,
                        ShouldFadeSimpleDoodleToDefaultWhenFetched) {
   EncodedLogo cached_logo;
   cached_logo.encoded_image = MakeRefPtr(kCachedB64);
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 70a4299..c4c5fa2 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/history/top_sites_factory.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/installable/installable_manager.h"
-#include "chrome/browser/language/chrome_language_detection_tab_helper.h"
 #include "chrome/browser/media/media_engagement_service.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_observer.h"
 #include "chrome/browser/metrics/oom/out_of_memory_reporter.h"
@@ -198,7 +197,6 @@
       web_contents, BookmarkModelFactory::GetForBrowserContext(
                         web_contents->GetBrowserContext()));
   chrome_browser_net::NetErrorTabHelper::CreateForWebContents(web_contents);
-  ChromeLanguageDetectionTabHelper::CreateForWebContents(web_contents);
   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       web_contents,
       autofill::ChromeAutofillClient::FromWebContents(web_contents));
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index f362d3eb..0371d02 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -184,11 +184,15 @@
          (icons - 1) * GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
 }
 
-size_t ToolbarActionsBar::WidthToIconCount(int pixels) const {
+size_t ToolbarActionsBar::WidthToIconCountUnclamped(int pixels) const {
   const int element_padding = GetLayoutConstant(TOOLBAR_ELEMENT_PADDING);
-  return base::ClampToRange(
-      (pixels + element_padding) / (GetViewSize().width() + element_padding), 0,
-      static_cast<int>(toolbar_actions_.size()));
+  return std::max(
+      (pixels + element_padding) / (GetViewSize().width() + element_padding),
+      0);
+}
+
+size_t ToolbarActionsBar::WidthToIconCount(int pixels) const {
+  return std::min(WidthToIconCountUnclamped(pixels), toolbar_actions_.size());
 }
 
 size_t ToolbarActionsBar::GetIconCount() const {
@@ -378,8 +382,10 @@
 
 void ToolbarActionsBar::SetOverflowRowWidth(int width) {
   DCHECK(in_overflow_mode());
+  // This uses the unclamped icon count to allow the in-menu bar to span the
+  // menu width.
   platform_settings_.icons_per_overflow_menu_row =
-      std::max(WidthToIconCount(width), static_cast<size_t>(1));
+      std::max(WidthToIconCountUnclamped(width), static_cast<size_t>(1));
 }
 
 void ToolbarActionsBar::OnResizeComplete(int width) {
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.h b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
index 424fe79d..1ffd09dc 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
@@ -271,6 +271,9 @@
   // are insetted. This defines the amount of paddings around the icon area.
   virtual gfx::Insets GetIconAreaInsets() const;
 
+  // Returns the number of icons that can fit within the given width.
+  size_t WidthToIconCountUnclamped(int width) const;
+
   // ToolbarActionsModel::Observer:
   void OnToolbarActionAdded(const ToolbarActionsModel::ToolbarItem& item,
                             int index) override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index a0dd85f..4730786 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -750,7 +750,7 @@
   // Home Launcher feature is enabled, since it gives the user the ability to
   // minimize all windows when pressing the Launcher button on the shelf.
   const bool hide_caption_buttons_in_tablet_mode =
-      !browser_view()->browser()->is_app();
+      !UsePackagedAppHeaderStyle(browser_view()->browser());
   if (hide_caption_buttons_in_tablet_mode && TabletModeClient::Get() &&
       TabletModeClient::Get()->tablet_mode_enabled()) {
     return false;
diff --git a/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc b/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
index 0ef98f8..dddf0a8 100644
--- a/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
+++ b/chrome/browser/ui/views/ime_driver/ime_driver_mus.cc
@@ -41,17 +41,18 @@
   ime_registrar->RegisterDriver(std::move(ime_driver_ptr));
 }
 
-void IMEDriver::StartSession(ws::mojom::StartSessionDetailsPtr details) {
+void IMEDriver::StartSession(ws::mojom::InputMethodRequest input_method_request,
+                             ws::mojom::TextInputClientPtr client,
+                             ws::mojom::SessionDetailsPtr details) {
 #if defined(OS_CHROMEOS)
   std::unique_ptr<RemoteTextInputClient> remote_client =
-      std::make_unique<RemoteTextInputClient>(
-          ws::mojom::TextInputClientPtr(std::move(details->client)),
-          std::move(details->state), details->caret_bounds);
+      std::make_unique<RemoteTextInputClient>(std::move(client),
+                                              std::move(details));
   mojo::MakeStrongBinding(
       std::make_unique<InputMethodBridge>(std::move(remote_client)),
-      std::move(details->input_method_request));
+      std::move(input_method_request));
 #else
   mojo::MakeStrongBinding(std::make_unique<SimpleInputMethod>(),
-                          std::move(details->input_method_request));
+                          std::move(input_method_request));
 #endif
 }
diff --git a/chrome/browser/ui/views/ime_driver/ime_driver_mus.h b/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
index 5357cfc..a4bb636 100644
--- a/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
+++ b/chrome/browser/ui/views/ime_driver/ime_driver_mus.h
@@ -19,7 +19,9 @@
 
  private:
   // ws::mojom::IMEDriver:
-  void StartSession(ws::mojom::StartSessionDetailsPtr details) override;
+  void StartSession(ws::mojom::InputMethodRequest input_method_request,
+                    ws::mojom::TextInputClientPtr client,
+                    ws::mojom::SessionDetailsPtr details) override;
 
   DISALLOW_COPY_AND_ASSIGN(IMEDriver);
 };
diff --git a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
index 3c63ad8..f9dacfb 100644
--- a/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
+++ b/chrome/browser/ui/views/ime_driver/input_method_bridge_chromeos_unittest.cc
@@ -111,15 +111,13 @@
 
     ws::mojom::TextInputClientPtr client_ptr;
     client_ = std::make_unique<TestTextInputClient>(MakeRequest(&client_ptr));
-    ws::mojom::TextInputStatePtr text_input_state =
-        ws::mojom::TextInputState::New();
-    text_input_state->text_input_type = ui::TEXT_INPUT_TYPE_TEXT;
-    text_input_state->text_input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
-    text_input_state->text_direction = base::i18n::LEFT_TO_RIGHT;
-    text_input_state->text_input_flags = 0;
+    ws::mojom::SessionDetailsPtr details = ws::mojom::SessionDetails::New();
+    details->state = ws::mojom::TextInputState::New(
+        ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT,
+        base::i18n::LEFT_TO_RIGHT, 0);
     input_method_ = std::make_unique<InputMethodBridge>(
-        std::make_unique<RemoteTextInputClient>(
-            std::move(client_ptr), std::move(text_input_state), gfx::Rect()));
+        std::make_unique<RemoteTextInputClient>(std::move(client_ptr),
+                                                std::move(details)));
   }
 
   bool ProcessKeyEvent(std::unique_ptr<ui::Event> event) {
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc b/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
index f6781f9..e7e21bc 100644
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
+++ b/chrome/browser/ui/views/ime_driver/remote_text_input_client.cc
@@ -9,12 +9,9 @@
 #include "ui/events/event_dispatcher.h"
 
 RemoteTextInputClient::RemoteTextInputClient(
-    ws::mojom::TextInputClientPtr remote_client,
-    ws::mojom::TextInputStatePtr text_input_state,
-    gfx::Rect caret_bounds)
-    : remote_client_(std::move(remote_client)),
-      text_input_state_(std::move(text_input_state)),
-      caret_bounds_(caret_bounds) {}
+    ws::mojom::TextInputClientPtr client,
+    ws::mojom::SessionDetailsPtr details)
+    : remote_client_(std::move(client)), details_(std::move(details)) {}
 
 RemoteTextInputClient::~RemoteTextInputClient() {
   while (!pending_callbacks_.empty())
@@ -23,11 +20,11 @@
 
 void RemoteTextInputClient::SetTextInputState(
     ws::mojom::TextInputStatePtr text_input_state) {
-  text_input_state_ = std::move(text_input_state);
+  details_->state = std::move(text_input_state);
 }
 
 void RemoteTextInputClient::SetCaretBounds(const gfx::Rect& caret_bounds) {
-  caret_bounds_ = caret_bounds;
+  details_->caret_bounds = caret_bounds;
 }
 
 void RemoteTextInputClient::OnDispatchKeyEventPostIMECompleted(bool completed) {
@@ -56,19 +53,19 @@
 }
 
 ui::TextInputType RemoteTextInputClient::GetTextInputType() const {
-  return text_input_state_->text_input_type;
+  return details_->state->text_input_type;
 }
 
 ui::TextInputMode RemoteTextInputClient::GetTextInputMode() const {
-  return text_input_state_->text_input_mode;
+  return details_->state->text_input_mode;
 }
 
 base::i18n::TextDirection RemoteTextInputClient::GetTextDirection() const {
-  return text_input_state_->text_direction;
+  return details_->state->text_direction;
 }
 
 int RemoteTextInputClient::GetTextInputFlags() const {
-  return text_input_state_->text_input_flags;
+  return details_->state->text_input_flags;
 }
 
 bool RemoteTextInputClient::CanComposeInline() const {
@@ -79,7 +76,7 @@
 }
 
 gfx::Rect RemoteTextInputClient::GetCaretBounds() const {
-  return caret_bounds_;
+  return details_->caret_bounds;
 }
 
 bool RemoteTextInputClient::GetCompositionCharacterBounds(
@@ -97,9 +94,7 @@
 }
 
 ui::TextInputClient::FocusReason RemoteTextInputClient::GetFocusReason() const {
-  // TODO(https://crbug.com/824604): Implement this correctly.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return ui::TextInputClient::FOCUS_REASON_OTHER;
+  return details_->focus_reason;
 }
 
 bool RemoteTextInputClient::GetTextRange(gfx::Range* range) const {
@@ -175,15 +170,11 @@
 }
 
 ukm::SourceId RemoteTextInputClient::GetClientSourceForMetrics() const {
-  // TODO(moshayedi): crbug.com/631527.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return ukm::SourceId();
+  return details_->client_source_for_metrics;
 }
 
 bool RemoteTextInputClient::ShouldDoLearning() {
-  // TODO(https://crbug.com/311180): Implement this method.
-  NOTIMPLEMENTED_LOG_ONCE();
-  return false;
+  return details_->should_do_learning;
 }
 
 ui::EventDispatchDetails RemoteTextInputClient::DispatchKeyEventPostIME(
diff --git a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
index 5f99d57..f256fe2 100644
--- a/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
+++ b/chrome/browser/ui/views/ime_driver/remote_text_input_client.h
@@ -17,9 +17,8 @@
 class RemoteTextInputClient : public ui::TextInputClient,
                               public ui::internal::InputMethodDelegate {
  public:
-  RemoteTextInputClient(ws::mojom::TextInputClientPtr remote_client,
-                        ws::mojom::TextInputStatePtr text_input_state,
-                        gfx::Rect caret_bounds);
+  RemoteTextInputClient(ws::mojom::TextInputClientPtr client,
+                        ws::mojom::SessionDetailsPtr details);
   ~RemoteTextInputClient() override;
 
   void SetTextInputState(ws::mojom::TextInputStatePtr text_input_state);
@@ -72,8 +71,7 @@
   void RunNextPendingCallback(bool completed);
 
   ws::mojom::TextInputClientPtr remote_client_;
-  ws::mojom::TextInputStatePtr text_input_state_;
-  gfx::Rect caret_bounds_;
+  ws::mojom::SessionDetailsPtr details_;
 
   // Callbacks supplied to DispatchKeyEventPostIME() are added here. When the
   // response from the remote side is received
diff --git a/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
index 8f7889f..d6c3b0f 100644
--- a/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_payment_app_browsertest.cc
@@ -106,7 +106,7 @@
 
   // Sets a TestDownloader for alicepay.com, bobpay.com and frankpay.com to
   // ServiceWorkerPaymentAppFactory, and ignores port in app scope.
-  void SetDownloaderAndIgnorePortInAppScopeForTesting() {
+  void SetDownloaderAndIgnorePortInOriginComparisonForTesting() {
     content::BrowserContext* context = browser()
                                            ->tab_strip_model()
                                            ->GetActiveWebContents()
@@ -121,7 +121,8 @@
     downloader->AddTestServerURL("https://frankpay.com/",
                                  frankpay_.GetURL("frankpay.com", "/"));
     ServiceWorkerPaymentAppFactory::GetInstance()
-        ->SetDownloaderAndIgnorePortInAppScopeForTesting(std::move(downloader));
+        ->SetDownloaderAndIgnorePortInOriginComparisonForTesting(
+            std::move(downloader));
   }
 
  private:
@@ -157,7 +158,7 @@
   InstallAlicePayForMethod("https://frankpay.com");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -169,8 +170,8 @@
     ExpectBodyContains({"false"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
                                  DialogEvent::PROCESSING_SPINNER_HIDDEN,
@@ -182,7 +183,7 @@
 
   // Repeat should have identical results.
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -194,8 +195,8 @@
     ExpectBodyContains({"false"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
                                  DialogEvent::PROCESSING_SPINNER_HIDDEN,
@@ -211,7 +212,7 @@
   InstallAlicePayForMethod("https://alicepay.com");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -223,8 +224,8 @@
     ExpectBodyContains({"true"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     InvokePaymentRequestUI();
 
@@ -236,7 +237,7 @@
 
   // Repeat should have identical results.
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -248,8 +249,8 @@
     ExpectBodyContains({"true"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     InvokePaymentRequestUI();
 
@@ -266,7 +267,7 @@
   InstallAlicePayForMethod("https://alicepay.com");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -278,8 +279,8 @@
     ExpectBodyContains({"true"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     InvokePaymentRequestUI();
 
@@ -291,7 +292,7 @@
 
   // Repeat should have identical results.
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -303,8 +304,8 @@
     ExpectBodyContains({"true"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     InvokePaymentRequestUI();
 
@@ -321,7 +322,7 @@
   BlockAlicePay();
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -333,8 +334,8 @@
     ExpectBodyContains({"false"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
                                  DialogEvent::PROCESSING_SPINNER_HIDDEN,
@@ -346,7 +347,7 @@
 
   // Repeat should have identical results.
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -358,8 +359,8 @@
     ExpectBodyContains({"false"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
                                  DialogEvent::PROCESSING_SPINNER_HIDDEN,
@@ -375,7 +376,7 @@
   InstallAlicePayForMethod("https://bobpay.com");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -387,8 +388,8 @@
     ExpectBodyContains({"false"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
                                  DialogEvent::PROCESSING_SPINNER_HIDDEN,
@@ -400,7 +401,7 @@
 
   // Repeat should have identical results.
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -412,8 +413,8 @@
     ExpectBodyContains({"false"});
 
     // A new payment request will be created below, so call
-    // SetDownloaderAndIgnorePortInAppScopeForTesting again.
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    // SetDownloaderAndIgnorePortInOriginComparisonForTesting again.
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
                                  DialogEvent::PROCESSING_SPINNER_HIDDEN,
@@ -429,7 +430,7 @@
   InstallAlicePayForMethod("basic-card");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo(
         "/payment_request_bobpay_and_basic_card_with_modifiers_test.html");
@@ -443,7 +444,7 @@
 
   // Repeat should have identical results.
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo(
         "/payment_request_bobpay_and_basic_card_with_modifiers_test.html");
@@ -467,7 +468,7 @@
   InstallBobPayForMethod("https://bobpay.com");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_ui_skip_test.html");
 
@@ -499,7 +500,7 @@
   InstallBobPayForMethod("https://bobpay.com");
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_test.html");
 
@@ -529,7 +530,7 @@
   AddAutofillProfile(profile);
 
   {
-    SetDownloaderAndIgnorePortInAppScopeForTesting();
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting();
 
     NavigateTo("/payment_request_bobpay_ui_skip_test.html");
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 42e549e..f70a5e2f 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -368,6 +368,8 @@
           Profile::FromWebUI(web_ui()));
   content::StoragePartition* partition =
       signin_partition_manager->GetCurrentStoragePartition();
+  if (!partition)
+    return;
 
   std::string gaps_cookie_value(kGAPSCookie);
   gaps_cookie_value += "=" + context.gaps_cookie;
diff --git a/chrome/browser/ui/webui/chromeos/smb_shares/smb_share_dialog.cc b/chrome/browser/ui/webui/chromeos/smb_shares/smb_share_dialog.cc
index 9102010..c85fa6d 100644
--- a/chrome/browser/ui/webui/chromeos/smb_shares/smb_share_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/smb_shares/smb_share_dialog.cc
@@ -19,7 +19,7 @@
 namespace smb_dialog {
 namespace {
 
-constexpr int kSmbShareDialogHeight = 442;
+constexpr int kSmbShareDialogHeight = 522;
 
 void AddSmbSharesStrings(content::WebUIDataSource* html_source) {
   // Add strings specific to smb_dialog.
diff --git a/chrome/browser/vr/browser_renderer.cc b/chrome/browser/vr/browser_renderer.cc
index 2494926..a1d21525 100644
--- a/chrome/browser/vr/browser_renderer.cc
+++ b/chrome/browser/vr/browser_renderer.cc
@@ -248,8 +248,8 @@
 }
 
 void BrowserRenderer::SetBrowserRendererBrowserInterfaceForTesting(
-    BrowserRendererBrowserInterface* interface) {
-  browser_ = interface;
+    BrowserRendererBrowserInterface* interface_ptr) {
+  browser_ = interface_ptr;
 }
 
 void BrowserRenderer::UpdateUi(const RenderInfo& render_info,
diff --git a/chrome/browser/vr/browser_renderer.h b/chrome/browser/vr/browser_renderer.h
index d3b6de7..4901e78 100644
--- a/chrome/browser/vr/browser_renderer.h
+++ b/chrome/browser/vr/browser_renderer.h
@@ -78,7 +78,7 @@
       VisibilityChangeExpectation visibility_expectation);
   void AcceptDoffPromptForTesting();
   void SetBrowserRendererBrowserInterfaceForTesting(
-      BrowserRendererBrowserInterface* interface);
+      BrowserRendererBrowserInterface* interface_ptr);
   void ConnectPresentingService(
       device::mojom::VRDisplayInfoPtr display_info,
       device::mojom::XRRuntimeSessionOptionsPtr options);
diff --git a/chrome/chrome_cleaner/interfaces/pup.mojom b/chrome/chrome_cleaner/interfaces/pup.mojom
index fa8ed33..b0e7f53 100644
--- a/chrome/chrome_cleaner/interfaces/pup.mojom
+++ b/chrome/chrome_cleaner/interfaces/pup.mojom
@@ -4,39 +4,8 @@
 
 module chrome_cleaner.mojom;
 
-import "chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom";
-import "chrome/chrome_cleaner/interfaces/windows_handle.mojom";
+// chrome_prompt included for the FilePath struct.
 import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
-import "mojo/public/mojom/base/string16.mojom";
-
-// Source:
-//   https://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx
-enum Wow64Access {
-  kNone = 0,
-  // KEY_WOW64_64KEY
-  k64Key = 0x0100,
-  // KEY_WOW64_32KEY
-  k32Key = 0x0200,
-};
-
-// Typemapped to chrome_cleaner::RegKeyPath.
-struct RegKeyPath {
-  PredefinedHandle rootkey;
-  // This is only sent by URZA, which currently doesn't support registry paths
-  // with embedded nulls.
-  mojo_base.mojom.String16 subkey;
-  Wow64Access wow64access;
-};
-
-// Used for reporting detected registry footprints.
-// Typemapped to chrome_cleaner::PUPData::RegistryFootprint.
-struct RegistryFootprint {
-  RegKeyPath key_path;
-  String16EmbeddedNulls value_name;
-  String16EmbeddedNulls value_substring;
-  // An enumerator of chrome_cleaner::RegistryMatchRule.
-  uint32 rule;
-};
 
 // Typemapped to chrome_cleaner::UwS::TraceLocation enumeration from
 // chrome_cleaner/logging/proto/shared_data.proto.
@@ -52,10 +21,11 @@
 };
 
 // Partially typemapped to chrome_cleaner::PUPData::PUP.
-// UwS signatures are not included.
+//
+// Only fields which are detected by the sandboxed engine are included. Other
+// fields of PUPData::PUP are populated directly in the broker process, so
+// there is no need to pass them over IPC.
 struct PUP {
   array<FilePath> expanded_disk_footprints;
-  array<RegistryFootprint> expanded_registry_footprints;
-  array<mojo_base.mojom.String16> expanded_scheduled_tasks;
   map<FilePath, FileInfo> disk_footprints_info;
 };
diff --git a/chrome/chrome_cleaner/interfaces/test_pup_typemap.mojom b/chrome/chrome_cleaner/interfaces/test_pup_typemap.mojom
index 0cc38c7f..421b0a8 100644
--- a/chrome/chrome_cleaner/interfaces/test_pup_typemap.mojom
+++ b/chrome/chrome_cleaner/interfaces/test_pup_typemap.mojom
@@ -9,8 +9,5 @@
 // Simple interface that echoes the structs defined in this file for testing the
 // corresponding typemaps.
 interface TestPUPTypemap {
-  EchoRegKeyPath(RegKeyPath in_reg) => (RegKeyPath out_reg);
-  EchoRegistryFootprint(RegistryFootprint in_fp)
-      => (RegistryFootprint out_fp);
   EchoPUP(PUP in_pup) => (PUP out_pup);
 };
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn b/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn
index ad355e3..4804a28 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn
+++ b/chrome/chrome_cleaner/interfaces/typemaps/BUILD.gn
@@ -17,6 +17,7 @@
     "//chrome/chrome_cleaner/ipc:mojo_task_runner",
     "//chrome/chrome_cleaner/strings",
     "//chrome/chrome_cleaner/strings:string_test_helpers",
+    "//chrome/chrome_cleaner/test:test_extensions",
     "//chrome/chrome_cleaner/test:test_util",
     "//components/chrome_cleaner/test:test_name_helper",
     "//mojo/core/embedder:embedder",
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/pup.typemap b/chrome/chrome_cleaner/interfaces/typemaps/pup.typemap
index d4ddfb4..f182035d6 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/pup.typemap
+++ b/chrome/chrome_cleaner/interfaces/typemaps/pup.typemap
@@ -4,10 +4,7 @@
 
 mojom = "//chrome/chrome_cleaner/interfaces/pup.mojom"
 
-public_headers = [
-  "//chrome/chrome_cleaner/os/registry.h",
-  "//chrome/chrome_cleaner/pup_data/pup_data.h",
-]
+public_headers = [ "//chrome/chrome_cleaner/pup_data/pup_data.h" ]
 
 public_deps = [
   "//chrome/chrome_cleaner/os:common_os",
@@ -24,8 +21,6 @@
 ]
 
 type_mappings = [
-  "chrome_cleaner.mojom.RegKeyPath=chrome_cleaner::RegKeyPath",
-  "chrome_cleaner.mojom.RegistryFootprint=chrome_cleaner::PUPData::RegistryFootprint",
   "chrome_cleaner.mojom.TraceLocation=chrome_cleaner::UwS::TraceLocation",
   "chrome_cleaner.mojom.PUP=chrome_cleaner::PUPData::PUP",
 ]
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.cc b/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.cc
index 08c0884..21e59de3 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.cc
+++ b/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.cc
@@ -4,13 +4,9 @@
 
 #include "chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.h"
 
-#include "chrome/chrome_cleaner/interfaces/typemaps/string16_embedded_nulls_mojom_traits.h"
-#include "chrome/chrome_cleaner/interfaces/typemaps/windows_handle_mojom_traits.h"
 #include "chrome/chrome_cleaner/logging/proto/shared_data.pb.h"
 #include "components/chrome_cleaner/public/typemaps/chrome_prompt_struct_traits.h"
-#include "mojo/public/cpp/base/string16_mojom_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
-#include "mojo/public/cpp/system/platform_handle.h"
 
 namespace mojo {
 
@@ -18,30 +14,12 @@
 using chrome_cleaner::mojom::FilePathDataView;
 using chrome_cleaner::mojom::PUPDataView;
 using chrome_cleaner::mojom::TraceLocationDataView;
-using chrome_cleaner::mojom::RegKeyPathDataView;
-using chrome_cleaner::mojom::RegistryFootprintDataView;
 using chrome_cleaner::FilePathSet;
 using chrome_cleaner::PUPData;
-using chrome_cleaner::RegKeyPath;
-using chrome_cleaner::RegistryMatchRule;
-using chrome_cleaner::String16EmbeddedNulls;
 using chrome_cleaner::UnorderedFilePathSet;
-using mojo_base::mojom::String16DataView;
 
 namespace {
 
-template <typename ValueDataView, typename Value>
-bool ReadFromArrayDataView(ArrayDataView<ValueDataView>* array_view,
-                           std::vector<Value>* out) {
-  for (size_t i = 0; i < array_view->size(); ++i) {
-    Value value;
-    if (!array_view->Read(i, &value))
-      return false;
-    out->push_back(value);
-  }
-  return true;
-}
-
 bool ReadFilePathSetFromArrayDataView(
     ArrayDataView<FilePathDataView>* array_view,
     FilePathSet* out) {
@@ -57,98 +35,6 @@
 }  // namespace
 
 // static
-HANDLE
-StructTraits<RegKeyPathDataView, RegKeyPath>::rootkey(
-    const RegKeyPath& reg_key_path) {
-  return reg_key_path.rootkey();
-}
-
-// static
-base::string16 StructTraits<RegKeyPathDataView, RegKeyPath>::subkey(
-    const RegKeyPath& reg_key_path) {
-  return reg_key_path.subkey();
-}
-
-// static
-chrome_cleaner::mojom::Wow64Access
-StructTraits<RegKeyPathDataView, RegKeyPath>::wow64access(
-    const RegKeyPath& reg_key_path) {
-  return static_cast<chrome_cleaner::mojom::Wow64Access>(
-      reg_key_path.wow64access());
-}
-
-// static
-bool StructTraits<RegKeyPathDataView, RegKeyPath>::Read(RegKeyPathDataView view,
-                                                        RegKeyPath* out) {
-  HANDLE rootkey;
-  if (!view.ReadRootkey(&rootkey))
-    return false;
-  base::string16 subkey;
-  if (!view.ReadSubkey(&subkey))
-    return false;
-
-  const REGSAM wow64access = static_cast<REGSAM>(view.wow64access());
-  if (wow64access != KEY_WOW64_32KEY && wow64access != KEY_WOW64_64KEY &&
-      wow64access != 0) {
-    return false;
-  }
-  *out = RegKeyPath(HKEY_LOCAL_MACHINE, subkey, wow64access);
-  return true;
-}
-
-// static
-RegKeyPath
-StructTraits<RegistryFootprintDataView, PUPData::RegistryFootprint>::key_path(
-    const PUPData::RegistryFootprint& reg_footprint) {
-  return reg_footprint.key_path;
-}
-
-// static
-String16EmbeddedNulls
-StructTraits<RegistryFootprintDataView, PUPData::RegistryFootprint>::value_name(
-    const PUPData::RegistryFootprint& reg_footprint) {
-  return String16EmbeddedNulls(reg_footprint.value_name);
-}
-
-// static
-String16EmbeddedNulls
-StructTraits<RegistryFootprintDataView, PUPData::RegistryFootprint>::
-    value_substring(const PUPData::RegistryFootprint& reg_footprint) {
-  return String16EmbeddedNulls(reg_footprint.value_substring);
-}
-
-// static
-uint32_t
-StructTraits<RegistryFootprintDataView, PUPData::RegistryFootprint>::rule(
-    const PUPData::RegistryFootprint& reg_footprint) {
-  return static_cast<uint32_t>(reg_footprint.rule);
-}
-
-// static
-bool StructTraits<RegistryFootprintDataView, PUPData::RegistryFootprint>::Read(
-    RegistryFootprintDataView view,
-    PUPData::RegistryFootprint* out) {
-  if (!view.ReadKeyPath(&out->key_path))
-    return false;
-
-  String16EmbeddedNulls value_name;
-  if (!view.ReadValueName(&value_name))
-    return false;
-  out->value_name = value_name.CastAsStringPiece16().as_string();
-
-  String16EmbeddedNulls value_substring;
-  if (!view.ReadValueSubstring(&value_substring))
-    return false;
-  out->value_substring = value_substring.CastAsStringPiece16().as_string();
-
-  if (!chrome_cleaner::RegistryMatchRule_IsValid(view.rule()))
-    return false;
-  out->rule = static_cast<RegistryMatchRule>(view.rule());
-
-  return true;
-}
-
-// static
 int32_t StructTraits<chrome_cleaner::mojom::TraceLocationDataView,
                      chrome_cleaner::UwS::TraceLocation>::
     value(const chrome_cleaner::UwS::TraceLocation& location) {
@@ -196,20 +82,6 @@
 }
 
 // static
-const std::vector<PUPData::RegistryFootprint>&
-StructTraits<PUPDataView, PUPData::PUP>::expanded_registry_footprints(
-    const PUPData::PUP& pup) {
-  return pup.expanded_registry_footprints;
-}
-
-// static
-const std::vector<base::string16>&
-StructTraits<PUPDataView, PUPData::PUP>::expanded_scheduled_tasks(
-    const PUPData::PUP& pup) {
-  return pup.expanded_scheduled_tasks;
-}
-
-// static
 const PUPData::PUP::FileInfoMap::MapType&
 StructTraits<PUPDataView, PUPData::PUP>::disk_footprints_info(
     const chrome_cleaner::PUPData::PUP& pup) {
@@ -226,19 +98,6 @@
     return false;
   }
 
-  ArrayDataView<RegistryFootprintDataView> reg_footprints_view;
-  view.GetExpandedRegistryFootprintsDataView(&reg_footprints_view);
-  if (!ReadFromArrayDataView(&reg_footprints_view,
-                             &out->expanded_registry_footprints)) {
-    return false;
-  }
-
-  ArrayDataView<String16DataView> tasks_view;
-  view.GetExpandedScheduledTasksDataView(&tasks_view);
-  if (!ReadFromArrayDataView(&tasks_view, &out->expanded_scheduled_tasks)) {
-    return false;
-  }
-
   MapDataView<FilePathDataView, FileInfoDataView> disk_footprints_info_view;
   view.GetDiskFootprintsInfoDataView(&disk_footprints_info_view);
   for (size_t i = 0; i < disk_footprints_info_view.size(); ++i) {
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.h b/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.h
index 80c23bc..ce23559 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.h
+++ b/chrome/chrome_cleaner/interfaces/typemaps/pup_struct_traits.h
@@ -7,51 +7,14 @@
 
 #include <stdint.h>
 
-#include "base/containers/span.h"
-#include "base/strings/string16.h"
 #include "chrome/chrome_cleaner/interfaces/pup.mojom.h"
-#include "chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom.h"
 #include "chrome/chrome_cleaner/os/file_path_set.h"
-#include "chrome/chrome_cleaner/os/registry.h"
-#include "mojo/public/cpp/bindings/array_traits.h"
+#include "mojo/public/cpp/bindings/array_traits_stl.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 
 namespace mojo {
 
 template <>
-struct StructTraits<chrome_cleaner::mojom::RegKeyPathDataView,
-                    chrome_cleaner::RegKeyPath> {
-  static HANDLE rootkey(const chrome_cleaner::RegKeyPath& reg_key_path);
-
-  static base::string16 subkey(const chrome_cleaner::RegKeyPath& reg_key_path);
-
-  static chrome_cleaner::mojom::Wow64Access wow64access(
-      const chrome_cleaner::RegKeyPath& reg_key_path);
-
-  static bool Read(chrome_cleaner::mojom::RegKeyPathDataView view,
-                   chrome_cleaner::RegKeyPath* out);
-};
-
-template <>
-struct StructTraits<chrome_cleaner::mojom::RegistryFootprintDataView,
-                    chrome_cleaner::PUPData::RegistryFootprint> {
-  static chrome_cleaner::RegKeyPath key_path(
-      const chrome_cleaner::PUPData::RegistryFootprint& reg_footprint);
-
-  static chrome_cleaner::String16EmbeddedNulls value_name(
-      const chrome_cleaner::PUPData::RegistryFootprint& reg_footprint);
-
-  static chrome_cleaner::String16EmbeddedNulls value_substring(
-      const chrome_cleaner::PUPData::RegistryFootprint& reg_footprint);
-
-  static uint32_t rule(
-      const chrome_cleaner::PUPData::RegistryFootprint& reg_footprint);
-
-  static bool Read(chrome_cleaner::mojom::RegistryFootprintDataView view,
-                   chrome_cleaner::PUPData::RegistryFootprint* out);
-};
-
-template <>
 struct StructTraits<chrome_cleaner::mojom::TraceLocationDataView,
                     chrome_cleaner::UwS::TraceLocation> {
   static int32_t value(const chrome_cleaner::UwS::TraceLocation& location);
@@ -78,16 +41,6 @@
   static const chrome_cleaner::UnorderedFilePathSet& expanded_disk_footprints(
       const chrome_cleaner::PUPData::PUP& pup);
 
-  // It's safe to return a reference, since the PUP object outlives the Mojo
-  // struct object.
-  static const std::vector<chrome_cleaner::PUPData::RegistryFootprint>&
-  expanded_registry_footprints(const chrome_cleaner::PUPData::PUP& pup);
-
-  // It's safe to return a reference, since the PUP object outlives the Mojo
-  // struct object.
-  static const std::vector<base::string16>& expanded_scheduled_tasks(
-      const chrome_cleaner::PUPData::PUP& pup);
-
   static const chrome_cleaner::PUPData::PUP::FileInfoMap::MapType&
   disk_footprints_info(const chrome_cleaner::PUPData::PUP& pup);
 
diff --git a/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc b/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc
index c67b81e..ff8b6af 100644
--- a/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc
+++ b/chrome/chrome_cleaner/interfaces/typemaps/pup_typemap_unittest.cc
@@ -9,6 +9,7 @@
 #include "chrome/chrome_cleaner/ipc/ipc_test_util.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
 #include "chrome/chrome_cleaner/pup_data/pup_data.h"
+#include "chrome/chrome_cleaner/test/test_extensions.h"
 #include "chrome/chrome_cleaner/test/test_util.h"
 #include "components/chrome_cleaner/test/test_name_helper.h"
 #include "mojo/core/embedder/embedder.h"
@@ -24,9 +25,6 @@
 using base::WaitableEvent;
 using testing::UnorderedElementsAreArray;
 
-constexpr wchar_t kWindowsCurrentVersionRegKeyName[] =
-    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion";
-
 // Special result code returned by the Mojo connection error handler on child
 // processes and expected by the parent process on typemap unmarshaling error
 // tests.
@@ -37,16 +35,6 @@
   explicit TestPUPTypemapImpl(mojom::TestPUPTypemapRequest request)
       : binding_(this, std::move(request)) {}
 
-  void EchoRegKeyPath(const RegKeyPath& path,
-                      EchoRegKeyPathCallback callback) override {
-    std::move(callback).Run(path);
-  }
-
-  void EchoRegistryFootprint(const PUPData::RegistryFootprint& reg_footprint,
-                             EchoRegistryFootprintCallback callback) override {
-    std::move(callback).Run(reg_footprint);
-  }
-
   void EchoPUP(const PUPData::PUP& pup, EchoPUPCallback callback) override {
     std::move(callback).Run(pup);
   }
@@ -96,18 +84,6 @@
     event->Signal();
   }
 
-  RegKeyPath EchoRegKeyPath(const RegKeyPath& input) {
-    return Echo(input, base::BindOnce(&EchoingChildProcess::RunEchoRegKeyPath,
-                                      base::Unretained(this)));
-  }
-
-  PUPData::RegistryFootprint EchoRegistryFootprint(
-      const PUPData::RegistryFootprint& input) {
-    return Echo(input,
-                base::BindOnce(&EchoingChildProcess::RunEchoRegistryFootprint,
-                               base::Unretained(this)));
-  }
-
   PUPData::PUP EchoPUP(const PUPData::PUP& input) {
     return Echo(input, base::BindOnce(&EchoingChildProcess::RunEchoPUP,
                                       base::Unretained(this)));
@@ -122,18 +98,6 @@
             base::Passed(&ptr_)));
   }
 
-  void RunEchoRegKeyPath(
-      const RegKeyPath& input,
-      mojom::TestPUPTypemap::EchoRegKeyPathCallback callback) {
-    (*ptr_)->EchoRegKeyPath(input, std::move(callback));
-  }
-
-  void RunEchoRegistryFootprint(
-      const PUPData::RegistryFootprint& input,
-      mojom::TestPUPTypemap::EchoRegistryFootprintCallback callback) {
-    (*ptr_)->EchoRegistryFootprint(input, std::move(callback));
-  }
-
   void RunEchoPUP(const PUPData::PUP& input,
                   mojom::TestPUPTypemap::EchoPUPCallback callback) {
     (*ptr_)->EchoPUP(input, std::move(callback));
@@ -189,58 +153,6 @@
   return child_process;
 }
 
-MULTIPROCESS_TEST_MAIN(EchoRegKeyPathMain) {
-  scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
-
-  RegKeyPath version_key(HKEY_LOCAL_MACHINE, kWindowsCurrentVersionRegKeyName,
-                         KEY_WOW64_32KEY);
-  EXPECT_EQ(version_key, child_process->EchoRegKeyPath(version_key));
-
-  return ::testing::Test::HasNonfatalFailure();
-}
-
-MULTIPROCESS_TEST_MAIN(EchoRegistryFootprint) {
-  scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
-
-  RegKeyPath version_key(HKEY_LOCAL_MACHINE, kWindowsCurrentVersionRegKeyName,
-                         KEY_WOW64_32KEY);
-
-  PUPData::RegistryFootprint fp1(version_key, L"value_name", L"value_substring",
-                                 REGISTRY_VALUE_MATCH_EXACT);
-  EXPECT_EQ(fp1, child_process->EchoRegistryFootprint(fp1));
-
-  PUPData::RegistryFootprint fp2(version_key, L"value_name", L"",
-                                 REGISTRY_VALUE_MATCH_EXACT);
-  EXPECT_EQ(fp2, child_process->EchoRegistryFootprint(fp2));
-
-  PUPData::RegistryFootprint fp3(version_key, L"", L"",
-                                 REGISTRY_VALUE_MATCH_EXACT);
-  EXPECT_EQ(fp3, child_process->EchoRegistryFootprint(fp3));
-
-  return ::testing::Test::HasNonfatalFailure();
-}
-
-MULTIPROCESS_TEST_MAIN(EchoRegistryFootprintFailure) {
-  scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
-
-  RegKeyPath version_key(HKEY_LOCAL_MACHINE, kWindowsCurrentVersionRegKeyName,
-                         KEY_WOW64_32KEY);
-
-  // Creates a RegistryFootprint with a rule that doesn't correspond to a valid
-  // RegistryMatchRule enum value. This will trigger a deserialization error on
-  // the broker process that will cause the pipe to be closed. As a consequence,
-  // the connection error handler, defined in BindToPipe(), will terminate the
-  // child process with a special exit code that will be expected to be received
-  // by the parent process.
-  PUPData::RegistryFootprint fp_invalid(version_key, L"value_name",
-                                        L"value_substring",
-                                        static_cast<RegistryMatchRule>(-1));
-  PUPData::RegistryFootprint unused =
-      child_process->EchoRegistryFootprint(fp_invalid);
-
-  return ::testing::Test::HasNonfatalFailure();
-}
-
 MULTIPROCESS_TEST_MAIN(EchoPUP) {
   scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
 
@@ -250,6 +162,38 @@
   pup.AddDiskFootprint(base::FilePath(L"C:\\Program Files\\File2.exe"));
   pup.AddDiskFootprint(base::FilePath(L"C:\\Program Files\\File3.exe"));
 
+  pup.disk_footprints_info.Insert(base::FilePath(L"C:\\File1.exe"),
+                                  PUPData::FileInfo({UwS::FOUND_IN_MEMORY}));
+  pup.disk_footprints_info.Insert(
+      base::FilePath(L"C:\\File2.exe"),
+      PUPData::FileInfo({UwS::FOUND_IN_SHELL, UwS::FOUND_IN_CLSID}));
+
+  const PUPData::PUP echoed = child_process->EchoPUP(pup);
+
+  // Not using operator== because error messages only show the sequences of
+  // bytes for each object, which makes it very hard to identify the differences
+  // between both objects.
+  EXPECT_THAT(
+      echoed.expanded_disk_footprints.file_paths(),
+      UnorderedElementsAreArray(pup.expanded_disk_footprints.file_paths()));
+  EXPECT_EQ(pup.disk_footprints_info.map(), echoed.disk_footprints_info.map());
+
+  return ::testing::Test::HasNonfatalFailure();
+}
+
+MULTIPROCESS_TEST_MAIN(EchoPUP_ExtraData) {
+  scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
+
+  PUPData::PUP pup;
+
+  pup.AddDiskFootprint(base::FilePath(L"C:\\Program Files\\File1.exe"));
+
+  // Add some items which are not normally populated in the target process.
+  // Expect them not to be present after echoing the PUP, to validate that they
+  // aren't passed through the Mojo interface. This keeps the security boundary
+  // small.
+  constexpr wchar_t kWindowsCurrentVersionRegKeyName[] =
+      L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion";
   RegKeyPath version_key(HKEY_LOCAL_MACHINE, kWindowsCurrentVersionRegKeyName,
                          KEY_WOW64_32KEY);
   pup.expanded_registry_footprints.emplace_back(version_key, L"value_name",
@@ -262,46 +206,22 @@
   pup.expanded_scheduled_tasks.push_back(L"Scheduled task 2");
   pup.expanded_scheduled_tasks.push_back(L"Scheduled task 3");
 
-  pup.disk_footprints_info.Insert(base::FilePath(L"C:\\File1.exe"),
-                                  PUPData::FileInfo({UwS::FOUND_IN_MEMORY}));
-  pup.disk_footprints_info.Insert(
-      base::FilePath(L"C:\\File2.exe"),
-      PUPData::FileInfo({UwS::FOUND_IN_SHELL, UwS::FOUND_IN_CLSID}));
+  pup.matched_extensions.push_back(ForceInstalledExtension(
+      *ExtensionID::Create(base::UTF16ToUTF8(kTestExtensionId1)),
+      POLICY_EXTENSION_FORCELIST, "https://test.com",
+      "all_your_permission_are_belong_to_us"));
 
   const PUPData::PUP echoed = child_process->EchoPUP(pup);
+
   // Not using operator== because error messages only show the sequences of
   // bytes for each object, which makes it very hard to identify the differences
   // between both objects.
   EXPECT_THAT(
       echoed.expanded_disk_footprints.file_paths(),
       UnorderedElementsAreArray(pup.expanded_disk_footprints.file_paths()));
-  EXPECT_THAT(echoed.expanded_registry_footprints,
-              UnorderedElementsAreArray(pup.expanded_registry_footprints));
-  EXPECT_THAT(echoed.expanded_scheduled_tasks,
-              UnorderedElementsAreArray(pup.expanded_scheduled_tasks));
-  EXPECT_EQ(pup.disk_footprints_info.map(), echoed.disk_footprints_info.map());
-
-  return ::testing::Test::HasNonfatalFailure();
-}
-
-MULTIPROCESS_TEST_MAIN(EchoPUPFailure_InvalidRegistryMatchRule) {
-  scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
-
-  // Same approach as used in EchoRegistryFootprintFailure, creates a registry
-  // footprint in the pup data with a rule that doesn't correspond to a valid
-  // RegistryMatchRule enum value. This will trigger a deserialization error on
-  // the broker process that will cause the pipe to be closed. As a consequence,
-  // the connection error handler, defined in BindToPipe(), will terminate the
-  // child process with a special exit code that will be expected to be received
-  // by the parent process.
-  PUPData::PUP pup;
-  RegKeyPath version_key(HKEY_LOCAL_MACHINE, kWindowsCurrentVersionRegKeyName,
-                         KEY_WOW64_32KEY);
-  pup.expanded_registry_footprints.emplace_back(
-      version_key, L"value_name", L"value_substring",
-      static_cast<RegistryMatchRule>(-1));
-
-  const PUPData::PUP unused = child_process->EchoPUP(pup);
+  EXPECT_TRUE(echoed.expanded_registry_footprints.empty());
+  EXPECT_TRUE(echoed.expanded_scheduled_tasks.empty());
+  EXPECT_TRUE(echoed.matched_extensions.empty());
 
   return ::testing::Test::HasNonfatalFailure();
 }
@@ -309,8 +229,12 @@
 MULTIPROCESS_TEST_MAIN(EchoPUPFailure_InvalidTraceLocation) {
   scoped_refptr<EchoingChildProcess> child_process = InitChildProcess();
 
-  // Same approach as used in EchoPUPFailure_InvalidRegistryMatchRule, this time
-  // passing an invalid trace location.
+  // Creates a PUP with a trace location that doesn't correspond to a valid
+  // UwS::TraceLocation enum value. This will trigger a deserialization error on
+  // the broker process that will cause the pipe to be closed. As a consequence,
+  // the connection error handler, defined in BindToPipe(), will terminate the
+  // child process with a special exit code that will be expected to be received
+  // by the parent process.
   PUPData::PUP pup;
   pup.disk_footprints_info.Insert(
       base::FilePath(L"C:\\File1.exe"),
@@ -329,7 +253,7 @@
 //    to fail;
 //  - child_main_function_: the name of the MULTIPROCESS_TEST_MAIN function for
 //    the child process.
-typedef std::tuple<bool, const char*> PUPTypemapTestParams;
+typedef std::tuple<bool, std::string> PUPTypemapTestParams;
 
 class PUPTypemapTest : public ::testing::TestWithParam<PUPTypemapTestParams> {
  public:
@@ -342,7 +266,7 @@
   }
 
  protected:
-  const char* child_main_function_;
+  std::string child_main_function_;
   bool expected_to_succeed_;
 
   scoped_refptr<MojoTaskRunner> mojo_task_runner_;
@@ -356,22 +280,18 @@
   EXPECT_EQ(expected_to_succeed_ ? 0 : kConnectionBrokenResultCode, exit_code);
 }
 
-INSTANTIATE_TEST_CASE_P(
-    Success,
-    PUPTypemapTest,
-    testing::Combine(testing::Values(true),
-                     testing::Values("EchoRegKeyPathMain",
-                                     "EchoRegistryFootprint",
-                                     "EchoPUP")),
-    GetParamNameForTest());
+INSTANTIATE_TEST_CASE_P(Success,
+                        PUPTypemapTest,
+                        testing::Combine(testing::Values(true),
+                                         testing::Values("EchoPUP",
+                                                         "EchoPUP_ExtraData")),
+                        GetParamNameForTest());
 
 INSTANTIATE_TEST_CASE_P(
     Failure,
     PUPTypemapTest,
     testing::Combine(testing::Values(false),
-                     testing::Values("EchoRegistryFootprintFailure",
-                                     "EchoPUPFailure_InvalidRegistryMatchRule",
-                                     "EchoPUPFailure_InvalidTraceLocation")),
+                     testing::Values("EchoPUPFailure_InvalidTraceLocation")),
     GetParamNameForTest());
 
 }  // namespace
diff --git a/chrome/chrome_cleaner/ipc/ipc_test_util.cc b/chrome/chrome_cleaner/ipc/ipc_test_util.cc
index 690058e1..db4e5ca 100644
--- a/chrome/chrome_cleaner/ipc/ipc_test_util.cc
+++ b/chrome/chrome_cleaner/ipc/ipc_test_util.cc
@@ -206,6 +206,10 @@
                                  mojo_channel_.TakeLocalEndpoint());
 }
 
+scoped_refptr<MojoTaskRunner> ParentProcess::mojo_task_runner() {
+  return mojo_task_runner_;
+}
+
 SandboxedParentProcess::SandboxedParentProcess(
     scoped_refptr<MojoTaskRunner> mojo_task_runner)
     : ParentProcess(mojo_task_runner) {}
diff --git a/chrome/chrome_cleaner/ipc/ipc_test_util.h b/chrome/chrome_cleaner/ipc/ipc_test_util.h
index cfd0fca..353d35d 100644
--- a/chrome/chrome_cleaner/ipc/ipc_test_util.h
+++ b/chrome/chrome_cleaner/ipc/ipc_test_util.h
@@ -64,6 +64,8 @@
   virtual bool PrepareAndLaunchTestChildProcess(
       const std::string& child_main_function);
 
+  scoped_refptr<MojoTaskRunner> mojo_task_runner();
+
   base::CommandLine command_line_;
 
  private:
diff --git a/chrome/chrome_cleaner/ipc/sandbox.cc b/chrome/chrome_cleaner/ipc/sandbox.cc
index 6ebd587..87265db 100644
--- a/chrome/chrome_cleaner/ipc/sandbox.cc
+++ b/chrome/chrome_cleaner/ipc/sandbox.cc
@@ -275,8 +275,8 @@
             << command_line.GetArgumentsString();
   sandbox::ResultCode sandbox_result = sandbox_broker_services->SpawnTarget(
       command_line.GetProgram().value().c_str(),
-      command_line.GetCommandLineString().c_str(), policy.get(),
-      &last_result_code, &last_win_error, &temp_process_info);
+      command_line.GetCommandLineString().c_str(), policy, &last_result_code,
+      &last_win_error, &temp_process_info);
   if (sandbox_result != sandbox::SBOX_ALL_OK) {
     LOG(DFATAL) << "Failed to spawn sandbox target: " << sandbox_result
                 << " , last sandbox result : " << last_result_code
@@ -408,8 +408,6 @@
       result_code = RESULT_CODE_ESET_SANDBOX_DISCONNECTED_TOO_SOON;
       break;
     case SandboxType::kParser:
-      // TODO(joenotcharles): This needs to be renamed to
-      // RESULT_CODE_PARSER_SANDBOX_DISCONNECTED_TOO_SOON.
       result_code = RESULT_CODE_PARSER_SANDBOX_DISCONNECTED_TOO_SOON;
       break;
     case SandboxType::kZipArchiver:
diff --git a/chrome/chrome_cleaner/os/process.cc b/chrome/chrome_cleaner/os/process.cc
index 6b4956d..5aad5ca 100644
--- a/chrome/chrome_cleaner/os/process.cc
+++ b/chrome/chrome_cleaner/os/process.cc
@@ -100,7 +100,7 @@
 
   PROCESS_MEMORY_COUNTERS pmc;
   if (::GetProcessMemoryInfo(::GetCurrentProcess(), &pmc, sizeof(pmc))) {
-    stats->peak_working_set_size = pmc.PeakWorkingSetSize / 1024;
+    stats->peak_working_set_size = pmc.PeakWorkingSetSize;
   }
 
   return true;
diff --git a/chrome/chrome_cleaner/os/process.h b/chrome/chrome_cleaner/os/process.h
index 92208d36..813d97cd 100644
--- a/chrome/chrome_cleaner/os/process.h
+++ b/chrome/chrome_cleaner/os/process.h
@@ -19,7 +19,7 @@
   base::IoCounters io_counters;
   base::TimeDelta user_time;
   base::TimeDelta kernel_time;
-  size_t peak_working_set_size;
+  size_t peak_working_set_size;  // In bytes.
 };
 
 // This returns a string instead of a base::FilePath because it is called from
diff --git a/chrome/chrome_cleaner/os/registry.cc b/chrome/chrome_cleaner/os/registry.cc
index 65185a7..30ee69d8 100644
--- a/chrome/chrome_cleaner/os/registry.cc
+++ b/chrome/chrome_cleaner/os/registry.cc
@@ -29,15 +29,15 @@
     return L"<unknown>";
 }
 
-bool IsPredefinedRegistryRootKey(HKEY key) {
+}  // namespace
+
+bool IsPredefinedRegistryHandle(HANDLE key) {
   return key == nullptr || key == INVALID_HANDLE_VALUE ||
          key == HKEY_CLASSES_ROOT || key == HKEY_CURRENT_CONFIG ||
          key == HKEY_CURRENT_USER || key == HKEY_LOCAL_MACHINE ||
          key == HKEY_USERS;
 }
 
-}  // namespace
-
 bool GetNativeKeyPath(const base::win::RegKey& key,
                       base::string16* native_key_path) {
   // This function uses a native API to determine the key path seen by the
@@ -75,7 +75,7 @@
 
 RegKeyPath::RegKeyPath(HKEY rootkey, const base::string16& subkey)
     : RegKeyPath(rootkey, subkey, 0) {
-  DCHECK(IsPredefinedRegistryRootKey(rootkey));
+  DCHECK(IsPredefinedRegistryHandle(rootkey));
 }
 
 RegKeyPath::RegKeyPath(HKEY rootkey,
@@ -83,7 +83,7 @@
                        REGSAM wow64access)
     : rootkey_(rootkey), subkey_(subkey), wow64access_(wow64access) {
   DCHECK_NE(static_cast<HKEY>(nullptr), rootkey_);
-  DCHECK(IsPredefinedRegistryRootKey(rootkey));
+  DCHECK(IsPredefinedRegistryHandle(rootkey));
   DCHECK(wow64access_ == KEY_WOW64_32KEY || wow64access_ == KEY_WOW64_64KEY ||
          wow64access_ == 0);
 }
diff --git a/chrome/chrome_cleaner/os/registry.h b/chrome/chrome_cleaner/os/registry.h
index 4338a0a..bd9aac1c 100644
--- a/chrome/chrome_cleaner/os/registry.h
+++ b/chrome/chrome_cleaner/os/registry.h
@@ -24,6 +24,10 @@
 bool GetNativeKeyPath(const base::win::RegKey& key,
                       base::string16* native_key_path);
 
+// Returns true for predefined handles, such as NULL, INVALID_HANDLE_VALUE, and
+// for predefined registry root keys, such as HKEY_CLASSES_ROOT.
+bool IsPredefinedRegistryHandle(HANDLE key);
+
 // Utility class to store a registry key path. Unlike a RegKey, this class is
 // copy/moveable.
 // It stores |rootkey|, which must be a predefined registry root key.
diff --git a/chrome/chrome_cleaner/pup_data/dynamic_pup_unittest.cc b/chrome/chrome_cleaner/pup_data/dynamic_pup_unittest.cc
index 340505a6..b6d7f86 100644
--- a/chrome/chrome_cleaner/pup_data/dynamic_pup_unittest.cc
+++ b/chrome/chrome_cleaner/pup_data/dynamic_pup_unittest.cc
@@ -22,8 +22,8 @@
 }  // namespace
 
 TEST(DynamicPUPTest, OnePUP) {
-  DynamicPUP pupOne(kTestUwSOneName, kTestUwSOneId,
-                    PUPData::FLAGS_STATE_CONFIRMED_UWS);
+  DynamicPUP pupOne(
+      kTestUwSOneName, kTestUwSOneId, PUPData::FLAGS_STATE_CONFIRMED_UWS);
 
   EXPECT_EQ(pupOne.signature().id, kTestUwSOneId);
   EXPECT_STREQ(pupOne.signature().name, kTestUwSOneName);
@@ -34,8 +34,8 @@
 }
 
 TEST(DynamicPUPTest, MultiPUPs) {
-  DynamicPUP pupOne(kTestUwSOneName, kTestUwSOneId,
-                    PUPData::FLAGS_STATE_CONFIRMED_UWS);
+  DynamicPUP pupOne(
+      kTestUwSOneName, kTestUwSOneId, PUPData::FLAGS_STATE_CONFIRMED_UWS);
   DynamicPUP pupTwo(kTestUwSTwoName, kTestUwSTwoId, PUPData::FLAGS_NONE);
 
   EXPECT_EQ(pupOne.signature().id, kTestUwSOneId);
diff --git a/chrome/chrome_cleaner/pup_data/pup_cleaner_util.cc b/chrome/chrome_cleaner/pup_data/pup_cleaner_util.cc
index 4b3e784..fccce79f 100644
--- a/chrome/chrome_cleaner/pup_data/pup_cleaner_util.cc
+++ b/chrome/chrome_cleaner/pup_data/pup_cleaner_util.cc
@@ -25,8 +25,8 @@
 
     for (const auto& file_path : pup->expanded_disk_footprints.file_paths()) {
       // Verify that files can be deleted.
-      if (FileRemover::IsFileRemovalAllowed(file_path,
-                                            files_detected_in_services, {}) ==
+      if (FileRemover::IsFileRemovalAllowed(
+              file_path, files_detected_in_services, {}) ==
           FileRemoverAPI::DeletionValidationStatus::ALLOWED) {
         pup_files->Insert(file_path);
         ++added_pup_files_size;
diff --git a/chrome/chrome_cleaner/pup_data/pup_cleaner_util_unittest.cc b/chrome/chrome_cleaner/pup_data/pup_cleaner_util_unittest.cc
index c23bfcc..d82bebed 100644
--- a/chrome/chrome_cleaner/pup_data/pup_cleaner_util_unittest.cc
+++ b/chrome/chrome_cleaner/pup_data/pup_cleaner_util_unittest.cc
@@ -29,8 +29,8 @@
  public:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    ASSERT_TRUE(base::CreateTemporaryDirInDir(temp_dir_.GetPath(), L"subfolder",
-                                              &subfolder_path_));
+    ASSERT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), L"subfolder", &subfolder_path_));
   }
 
   base::FilePath CreateFileInTopDir(const base::string16& basename,
@@ -55,9 +55,13 @@
 TEST_F(PUPCleanerUtilTest, CollectRemovablePupFiles_ActiveFiles) {
   PUPData pup_data;
   TestPUPData test_pup_data;
-  test_pup_data.AddPUP(kFakePupId1, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_pup_data.AddPUP(kFakePupId1,
+                       PUPData::FLAGS_ACTION_REMOVE,
+                       nullptr,
                        PUPData::kMaxFilesToRemoveSmallUwS);
-  test_pup_data.AddPUP(kFakePupId2, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_pup_data.AddPUP(kFakePupId2,
+                       PUPData::FLAGS_ACTION_REMOVE,
+                       nullptr,
                        PUPData::kMaxFilesToRemoveSmallUwS);
 
   base::FilePath active_path1 = CreateFileInTopDir(L"file.exe", kFileContent);
@@ -86,8 +90,8 @@
   expected_collected_paths.Insert(lnk_path);
 
   FilePathSet collected_paths;
-  EXPECT_TRUE(CollectRemovablePupFiles(Engine::URZA, {kFakePupId1, kFakePupId2},
-                                       &collected_paths));
+  EXPECT_TRUE(CollectRemovablePupFiles(
+      Engine::URZA, {kFakePupId1, kFakePupId2}, &collected_paths));
   EXPECT_EQ(expected_collected_paths, collected_paths);
 }
 
@@ -96,7 +100,9 @@
   //  detected as part of UwS service registration.
   PUPData pup_data;
   TestPUPData test_pup_data;
-  test_pup_data.AddPUP(kFakePupId1, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_pup_data.AddPUP(kFakePupId1,
+                       PUPData::FLAGS_ACTION_REMOVE,
+                       nullptr,
                        PUPData::kMaxFilesToRemoveSmallUwS);
 
   base::FilePath inactive_path = CreateFileInTopDir(L"file.jpg", kFileContent);
@@ -127,7 +133,9 @@
   // files for a PUP exceed the PUPs max_files_to_remove threshold.
   PUPData pup_data;
   TestPUPData test_pup_data;
-  test_pup_data.AddPUP(kFakePupId1, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_pup_data.AddPUP(kFakePupId1,
+                       PUPData::FLAGS_ACTION_REMOVE,
+                       nullptr,
                        /*max_files_to_remove=*/1);
   PUPData::PUP* pup = pup_data.GetPUP(kFakePupId1);
   ASSERT_TRUE(pup);
@@ -146,8 +154,8 @@
   expected_collected_paths.Insert(active_path2);
 
   FilePathSet collected_paths_unsafe;
-  EXPECT_FALSE(CollectRemovablePupFiles(Engine::URZA, {kFakePupId1},
-                                        &collected_paths_unsafe));
+  EXPECT_FALSE(CollectRemovablePupFiles(
+      Engine::URZA, {kFakePupId1}, &collected_paths_unsafe));
   EXPECT_EQ(expected_collected_paths, collected_paths_unsafe);
 }
 
diff --git a/chrome/chrome_cleaner/pup_data/pup_data.h b/chrome/chrome_cleaner/pup_data/pup_data.h
index 1b732cc..0972945 100644
--- a/chrome/chrome_cleaner/pup_data/pup_data.h
+++ b/chrome/chrome_cleaner/pup_data/pup_data.h
@@ -273,6 +273,13 @@
   };
 
   // The data that was matched for an UwS during scanning.
+  //
+  // PUP objects are created in the engine sandbox process as the engine finds
+  // UwS, and then copied to the broker process using the Mojo interface in
+  // interfaces/pup.mojom.
+  //
+  // For the legacy scanning engine PUP objects are created directly in the
+  // sandbox broker process.
   class PUP {
    public:
     typedef FilePathMap<FileInfo> FileInfoMap;
@@ -286,6 +293,9 @@
 
     PUP& operator=(const PUP& other);
 
+    // Static data for an UwS. This will be empty in the sandbox target
+    // process. In the broker process the UwSSignature is looked up by UwSId
+    // and added to the PUP structure.
     const UwSSignature& signature() const { return *signature_; }
 
     // Add the given |file_path| to |expanded_disk_footprints|. Return false if
@@ -300,18 +310,24 @@
     // Add all matching data from |other| to the current pup.
     void MergeFrom(const PUP& other);
 
-    // The set of expanded disk footprints generated by the scanner.
+    // The set of expanded disk footprints generated by the scanner. Populated
+    // in the target process when the engine is sandboxed.
     FilePathSet expanded_disk_footprints;
 
+    // The set of expanded registry footprints generated by the scanner. Only
+    // populated by the legacy unsandboxed engine.
     std::vector<RegistryFootprint> expanded_registry_footprints;
 
     // The list of expanded scheduled task names generated by the scanner.
+    // Only populated by the legacy unsandboxed engine.
     std::vector<base::string16> expanded_scheduled_tasks;
 
-    // Mapping from detected files to where they were found.
+    // Mapping from detected files to where they were found. Populated in the
+    // target process when the engine is sandboxed.
     FileInfoMap disk_footprints_info;
 
-    // List of UwE found by the scanner.
+    // List of UwE found by the scanner. Populated in the broker process after
+    // the PUPData is copied from the target process.
     std::vector<ForceInstalledExtension> matched_extensions;
 
    protected:
diff --git a/chrome/chrome_cleaner/pup_data/pup_data_unittest.cc b/chrome/chrome_cleaner/pup_data/pup_data_unittest.cc
index dffefc4..382abfe 100644
--- a/chrome/chrome_cleaner/pup_data/pup_data_unittest.cc
+++ b/chrome/chrome_cleaner/pup_data/pup_data_unittest.cc
@@ -204,20 +204,20 @@
 };
 
 TEST_F(PUPDataTest, OnePUP) {
-  test_data().AddPUP(k42ID, PUPData::FLAGS_NONE, k42Name,
-                     PUPData::kMaxFilesToRemoveSmallUwS);
+  test_data().AddPUP(
+      k42ID, PUPData::FLAGS_NONE, k42Name, PUPData::kMaxFilesToRemoveSmallUwS);
 
   const PUPData::UwSSignature& signature = PUPData::GetPUP(k42ID)->signature();
   EXPECT_STREQ(k42Name, signature.name);
 }
 
 TEST_F(PUPDataTest, MultiPUPs) {
-  test_data().AddPUP(k42ID, PUPData::FLAGS_NONE, k42Name,
-                     PUPData::kMaxFilesToRemoveSmallUwS);
-  test_data().AddPUP(k24ID, PUPData::FLAGS_NONE, k24Name,
-                     PUPData::kMaxFilesToRemoveSmallUwS);
-  test_data().AddPUP(k12ID, PUPData::FLAGS_NONE, k12Name,
-                     PUPData::kMaxFilesToRemoveSmallUwS);
+  test_data().AddPUP(
+      k42ID, PUPData::FLAGS_NONE, k42Name, PUPData::kMaxFilesToRemoveSmallUwS);
+  test_data().AddPUP(
+      k24ID, PUPData::FLAGS_NONE, k24Name, PUPData::kMaxFilesToRemoveSmallUwS);
+  test_data().AddPUP(
+      k12ID, PUPData::FLAGS_NONE, k12Name, PUPData::kMaxFilesToRemoveSmallUwS);
 
   const PUPData::UwSSignature& signature12 =
       PUPData::GetPUP(k12ID)->signature();
@@ -255,8 +255,11 @@
 }
 
 TEST_F(PUPDataTest, OneRegistry) {
-  test_data().AddRegistryFootprint(k42ID, REGISTRY_ROOT_USERS,
-                                   k42RegistryKeyPath, nullptr, nullptr,
+  test_data().AddRegistryFootprint(k42ID,
+                                   REGISTRY_ROOT_USERS,
+                                   k42RegistryKeyPath,
+                                   nullptr,
+                                   nullptr,
                                    REGISTRY_VALUE_MATCH_KEY);
 
   ExpectNumPUPs(1);
@@ -268,14 +271,22 @@
 }
 
 TEST_F(PUPDataTest, MultiRegistry) {
-  test_data().AddRegistryFootprint(k42ID, REGISTRY_ROOT_USERS,
-                                   k42RegistryKeyPath, nullptr, nullptr,
+  test_data().AddRegistryFootprint(k42ID,
+                                   REGISTRY_ROOT_USERS,
+                                   k42RegistryKeyPath,
+                                   nullptr,
+                                   nullptr,
                                    REGISTRY_VALUE_MATCH_KEY);
-  test_data().AddRegistryFootprint(k24ID, REGISTRY_ROOT_LOCAL_MACHINE,
-                                   k24RegistryKeyPath, k24RegistryValueName,
-                                   nullptr, REGISTRY_VALUE_MATCH_VALUE_NAME);
-  test_data().AddRegistryFootprint(k12ID, REGISTRY_ROOT_MACHINE_GROUP_POLICY,
-                                   k12RegistryKeyPath1, k12RegistryValueName1,
+  test_data().AddRegistryFootprint(k24ID,
+                                   REGISTRY_ROOT_LOCAL_MACHINE,
+                                   k24RegistryKeyPath,
+                                   k24RegistryValueName,
+                                   nullptr,
+                                   REGISTRY_VALUE_MATCH_VALUE_NAME);
+  test_data().AddRegistryFootprint(k12ID,
+                                   REGISTRY_ROOT_MACHINE_GROUP_POLICY,
+                                   k12RegistryKeyPath1,
+                                   k12RegistryValueName1,
                                    k12RegistryValueSubstring,
                                    REGISTRY_VALUE_MATCH_EXACT);
   ExpectNumPUPs(3);
@@ -290,12 +301,18 @@
   test_data().AddDiskFootprint(k42ID, k42CSIDL[2], k42RelativeDiskPath2,
                                PUPData::DISK_MATCH_ANY_FILE);
 
-  test_data().AddRegistryFootprint(k42ID, REGISTRY_ROOT_CLASSES,
-                                   k42RegistryKeyPath, nullptr, nullptr,
+  test_data().AddRegistryFootprint(k42ID,
+                                   REGISTRY_ROOT_CLASSES,
+                                   k42RegistryKeyPath,
+                                   nullptr,
+                                   nullptr,
                                    REGISTRY_VALUE_MATCH_KEY);
-  test_data().AddRegistryFootprint(k24ID, REGISTRY_ROOT_CLASSES,
-                                   k24RegistryKeyPath, k24RegistryValueName,
-                                   nullptr, REGISTRY_VALUE_MATCH_VALUE_NAME);
+  test_data().AddRegistryFootprint(k24ID,
+                                   REGISTRY_ROOT_CLASSES,
+                                   k24RegistryKeyPath,
+                                   k24RegistryValueName,
+                                   nullptr,
+                                   REGISTRY_VALUE_MATCH_VALUE_NAME);
   test_data().AddRegistryFootprint(k12ID, REGISTRY_ROOT_USERS_GROUP_POLICY,
                                    k12RegistryKeyPath1, k12RegistryValueName1,
                                    k12RegistryValueSubstring,
@@ -311,11 +328,17 @@
   test_data().AddDiskFootprint(k42ID, k42CSIDL[0], k42RelativeDiskPath1,
                                PUPData::DISK_MATCH_ANY_FILE);
 
-  test_data().AddRegistryFootprint(k12ID, REGISTRY_ROOT_USERS,
-                                   k12RegistryKeyPath2, k12RegistryValueName2,
-                                   nullptr, REGISTRY_VALUE_MATCH_VALUE_NAME);
-  test_data().AddRegistryFootprint(k12ID, REGISTRY_ROOT_USERS,
-                                   k12RegistryKeyPath2, nullptr, nullptr,
+  test_data().AddRegistryFootprint(k12ID,
+                                   REGISTRY_ROOT_USERS,
+                                   k12RegistryKeyPath2,
+                                   k12RegistryValueName2,
+                                   nullptr,
+                                   REGISTRY_VALUE_MATCH_VALUE_NAME);
+  test_data().AddRegistryFootprint(k12ID,
+                                   REGISTRY_ROOT_USERS,
+                                   k12RegistryKeyPath2,
+                                   nullptr,
+                                   nullptr,
                                    REGISTRY_VALUE_MATCH_KEY);
 
   ExpectNumPUPs(3);
@@ -386,23 +409,25 @@
 }
 
 TEST_F(PUPDataTest, ChoosePUPs) {
-  test_data().AddPUP(k12ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k12ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
-  test_data().AddPUP(k24ID, PUPData::FLAGS_NONE, nullptr,
-                     PUPData::kMaxFilesToRemoveSmallUwS);
+  test_data().AddPUP(
+      k24ID, PUPData::FLAGS_NONE, nullptr, PUPData::kMaxFilesToRemoveSmallUwS);
 
   std::vector<UwSId> found_pups;
   found_pups.push_back(k12ID);
   found_pups.push_back(k24ID);
 
   std::vector<UwSId> found_pups_to_report;
-  pup_data().ChoosePUPs(found_pups, &PUPData::HasReportOnlyFlag,
-                        &found_pups_to_report);
+  pup_data().ChoosePUPs(
+      found_pups, &PUPData::HasReportOnlyFlag, &found_pups_to_report);
   EXPECT_THAT(found_pups_to_report, testing::ElementsAre(k24ID));
 
   std::vector<UwSId> found_pups_to_remove;
-  pup_data().ChoosePUPs(found_pups, &PUPData::HasRemovalFlag,
-                        &found_pups_to_remove);
+  pup_data().ChoosePUPs(
+      found_pups, &PUPData::HasRemovalFlag, &found_pups_to_remove);
   EXPECT_THAT(found_pups_to_remove, testing::ElementsAre(k12ID));
 }
 
@@ -483,7 +508,9 @@
 }
 
 TEST_F(PUPDataTest, HasRemovalFlaggedPUP) {
-  test_data().AddPUP(k42ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k42ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   EXPECT_TRUE(ValidateFlags(&PUPData::HasRemovalFlag));
   EXPECT_FALSE(ValidateFlags(&PUPData::HasReportOnlyFlag));
@@ -491,15 +518,17 @@
 }
 
 TEST_F(PUPDataTest, HasReportOnlyFlaggedPUP) {
-  test_data().AddPUP(k42ID, PUPData::FLAGS_NONE, nullptr,
-                     PUPData::kMaxFilesToRemoveSmallUwS);
+  test_data().AddPUP(
+      k42ID, PUPData::FLAGS_NONE, nullptr, PUPData::kMaxFilesToRemoveSmallUwS);
   EXPECT_TRUE(ValidateFlags(&PUPData::HasReportOnlyFlag));
   EXPECT_FALSE(ValidateFlags(&PUPData::HasRemovalFlag));
   EXPECT_FALSE(ValidateFlags(&PUPData::HasRebootFlag));
 }
 
 TEST_F(PUPDataTest, HasRebootFlaggedPUP) {
-  test_data().AddPUP(k42ID, PUPData::FLAGS_REMOVAL_FORCE_REBOOT, nullptr,
+  test_data().AddPUP(k42ID,
+                     PUPData::FLAGS_REMOVAL_FORCE_REBOOT,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   EXPECT_TRUE(ValidateFlags(&PUPData::HasRebootFlag));
   EXPECT_FALSE(ValidateFlags(&PUPData::HasRemovalFlag));
@@ -508,17 +537,23 @@
 
 TEST_F(PUPDataTest, HasRebootAndRemoveFlaggedPUP) {
   test_data().AddPUP(
-      k42ID, PUPData::FLAGS_ACTION_REMOVE | PUPData::FLAGS_REMOVAL_FORCE_REBOOT,
-      nullptr, PUPData::kMaxFilesToRemoveSmallUwS);
+      k42ID,
+      PUPData::FLAGS_ACTION_REMOVE | PUPData::FLAGS_REMOVAL_FORCE_REBOOT,
+      nullptr,
+      PUPData::kMaxFilesToRemoveSmallUwS);
   EXPECT_TRUE(ValidateFlags(&PUPData::HasRebootFlag));
   EXPECT_TRUE(ValidateFlags(&PUPData::HasRemovalFlag));
   EXPECT_FALSE(ValidateFlags(&PUPData::HasReportOnlyFlag));
 }
 
 TEST_F(PUPDataTest, HasSomeFlaggedPUP) {
-  test_data().AddPUP(k12ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k12ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
-  test_data().AddPUP(k24ID, PUPData::FLAGS_REMOVAL_FORCE_REBOOT, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_REMOVAL_FORCE_REBOOT,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   EXPECT_TRUE(ValidateFlags(&PUPData::HasRemovalFlag));
   EXPECT_TRUE(ValidateFlags(&PUPData::HasReportOnlyFlag));
@@ -526,7 +561,9 @@
 }
 
 TEST_F(PUPDataTest, DeleteRegistryKeyAndValue) {
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   PUPData::PUP* pup = pup_data().GetPUP(k24ID);
 
@@ -536,25 +573,38 @@
 
   ExpectRegistryFootprint(*pup, key_path, L"", L"", REGISTRY_VALUE_MATCH_KEY);
 
-  ExpectRegistryFootprint(*pup, key_path, k24RegistryValueName, L"",
+  ExpectRegistryFootprint(*pup,
+                          key_path,
+                          k24RegistryValueName,
+                          L"",
                           REGISTRY_VALUE_MATCH_VALUE_NAME);
 }
 
 TEST_F(PUPDataTest, UpdateRegistryValue) {
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   PUPData::PUP* pup = pup_data().GetPUP(k24ID);
 
   const RegKeyPath key_path(HKEY_LOCAL_MACHINE, k24RegistryKeyPath);
-  PUPData::UpdateRegistryValue(key_path, k24RegistryValueName, k24RegistryValue,
-                               REGISTRY_VALUE_MATCH_PARTIAL, pup);
+  PUPData::UpdateRegistryValue(key_path,
+                               k24RegistryValueName,
+                               k24RegistryValue,
+                               REGISTRY_VALUE_MATCH_PARTIAL,
+                               pup);
 
-  ExpectRegistryFootprint(*pup, key_path, k24RegistryValueName,
-                          k24RegistryValue, REGISTRY_VALUE_MATCH_PARTIAL);
+  ExpectRegistryFootprint(*pup,
+                          key_path,
+                          k24RegistryValueName,
+                          k24RegistryValue,
+                          REGISTRY_VALUE_MATCH_PARTIAL);
 }
 
 TEST_F(PUPDataTest, DeleteScheduledTask) {
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   PUPData::PUP* pup = pup_data().GetPUP(k24ID);
 
@@ -565,10 +615,14 @@
 }
 
 TEST_F(PUPDataTest, GetAllPUPs) {
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
 
-  test_data().AddPUP(k42ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k42ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
 
   PUPData::InitializePUPData({});
@@ -588,10 +642,14 @@
 }
 
 TEST_F(PUPDataTest, GetUwSIds) {
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
 
-  test_data().AddPUP(k42ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k42ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
 
   PUPData::InitializePUPData({});
@@ -603,12 +661,16 @@
 }
 
 TEST_F(PUPDataTest, GetFilesDetectedInServices) {
-  test_data().AddPUP(k12ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k12ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   pup_data().GetPUP(k12ID)->AddDiskFootprintTraceLocation(
       base::FilePath(k12DiskPath), UwS::FOUND_IN_SERVICE);
 
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
   pup_data().GetPUP(k24ID)->AddDiskFootprintTraceLocation(
       base::FilePath(k42AbsoluteDiskPath), UwS::FOUND_IN_MEMORY);
@@ -623,7 +685,9 @@
   PUPData::InitializePUPData({});
   EXPECT_EQ(PUPData::GetUwSIds()->size(), 0UL);
 
-  test_data().AddPUP(k24ID, PUPData::FLAGS_ACTION_REMOVE, nullptr,
+  test_data().AddPUP(k24ID,
+                     PUPData::FLAGS_ACTION_REMOVE,
+                     nullptr,
                      PUPData::kMaxFilesToRemoveSmallUwS);
 
   PUPData::InitializePUPData({});
diff --git a/chrome/chrome_cleaner/pup_data/pup_disk_util.cc b/chrome/chrome_cleaner/pup_data/pup_disk_util.cc
index eb00396..5cf1cf4 100644
--- a/chrome/chrome_cleaner/pup_data/pup_disk_util.cc
+++ b/chrome/chrome_cleaner/pup_data/pup_disk_util.cc
@@ -22,7 +22,8 @@
     return;
 
   base::FileEnumerator file_enum(
-      file_path, true,
+      file_path,
+      true,
       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
   for (base::FilePath file = file_enum.Next(); !file.empty();
        file = file_enum.Next()) {
@@ -35,7 +36,9 @@
                                        size_t max_filesize,
                                        bool allow_folders,
                                        PUPData::PUP* pup) {
-  return CollectPathsRecursivelyWithLimits(file_path, max_files, max_filesize,
+  return CollectPathsRecursivelyWithLimits(file_path,
+                                           max_files,
+                                           max_filesize,
                                            allow_folders,
                                            &pup->expanded_disk_footprints);
 }
diff --git a/chrome/chrome_cleaner/pup_data/pup_disk_util_unittest.cc b/chrome/chrome_cleaner/pup_data/pup_disk_util_unittest.cc
index e3b55fb..2c108d1f 100644
--- a/chrome/chrome_cleaner/pup_data/pup_disk_util_unittest.cc
+++ b/chrome/chrome_cleaner/pup_data/pup_disk_util_unittest.cc
@@ -34,8 +34,8 @@
  public:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    ASSERT_TRUE(base::CreateTemporaryDirInDir(temp_dir_.GetPath(), kSubFolder,
-                                              &subfolder_path_));
+    ASSERT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), kSubFolder, &subfolder_path_));
   }
 
   base::FilePath CreateFileInTopDir(const base::FilePath& basename,
diff --git a/chrome/common/safe_browsing/rar_analyzer.cc b/chrome/common/safe_browsing/rar_analyzer.cc
index 1d352338..b8d56f05 100644
--- a/chrome/common/safe_browsing/rar_analyzer.cc
+++ b/chrome/common/safe_browsing/rar_analyzer.cc
@@ -47,7 +47,12 @@
     return;
   }
 
-  if (base::FeatureList::IsEnabled(kInspectRarContentFeature)) {
+  // If the file is too big to unpack, fall back to the old method.
+  bool too_big_to_unpack =
+      base::checked_cast<uint64_t>(rar_file.GetLength()) >
+      FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze("rar");
+  if (base::FeatureList::IsEnabled(kInspectRarContentFeature) &&
+      !too_big_to_unpack) {
     auto command = std::make_unique<third_party_unrar::CommandData>();
     command->ParseArg(const_cast<wchar_t*>(L"-p"));
     command->ParseArg(const_cast<wchar_t*>(L"x"));
diff --git a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc
index ea6c8ea0..69c35e0 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/services/file_util/public/mojom/constants.mojom.h"
@@ -16,9 +17,13 @@
 
 SandboxedDMGAnalyzer::SandboxedDMGAnalyzer(
     const base::FilePath& dmg_file,
+    const uint64_t max_size,
     const ResultCallback& callback,
     service_manager::Connector* connector)
-    : file_path_(dmg_file), callback_(callback), connector_(connector) {
+    : file_path_(dmg_file),
+      max_size_(max_size),
+      callback_(callback),
+      connector_(connector) {
   DCHECK(callback);
 }
 
@@ -45,6 +50,18 @@
     return;
   }
 
+  uint64_t size = file.GetLength();
+
+  bool too_big_to_unpack = base::checked_cast<uint64_t>(size) > max_size_;
+  UMA_HISTOGRAM_BOOLEAN("SBClientDownload.DmgTooBigToUnpack",
+                        too_big_to_unpack);
+
+  if (too_big_to_unpack) {
+    DLOG(ERROR) << "File is too big: " << file_path_.value();
+    ReportFileFailure();
+    return;
+  }
+
   base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
                            base::Bind(&SandboxedDMGAnalyzer::AnalyzeFile, this,
                                       base::Passed(&file)));
diff --git a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h
index 06ddd62..f8c790c 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h
+++ b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h
@@ -30,6 +30,7 @@
       base::Callback<void(const safe_browsing::ArchiveAnalyzerResults&)>;
 
   SandboxedDMGAnalyzer(const base::FilePath& dmg_file,
+                       const uint64_t max_size,
                        const ResultCallback& callback,
                        service_manager::Connector* connector);
 
@@ -56,6 +57,9 @@
   // The file path of the file to analyze.
   const base::FilePath file_path_;
 
+  // Maximum file size allowed by FilePolicy
+  const uint64_t max_size_;
+
   // Callback invoked on the UI thread with the file analyze results.
   const ResultCallback callback_;
 
diff --git a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac_unittest.cc b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac_unittest.cc
index 6ebc6a8..bd35d0a 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac_unittest.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
+#include "chrome/common/safe_browsing/file_type_policies.h"
 #include "chrome/services/file_util/file_util_service.h"
 #include "chrome/services/file_util/public/mojom/constants.mojom.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -37,7 +38,10 @@
     base::RunLoop run_loop;
     ResultsGetter results_getter(run_loop.QuitClosure(), results);
     scoped_refptr<SandboxedDMGAnalyzer> analyzer(new SandboxedDMGAnalyzer(
-        path, results_getter.GetCallback(),
+        path,
+        safe_browsing::FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze(
+            "dmg"),
+        results_getter.GetCallback(),
         test_connector_factory_.GetDefaultConnector()));
     analyzer->Start();
     run_loop.Run();
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b4bac52..77a2699 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -763,6 +763,7 @@
       "../browser/prerender/prerender_nostate_prefetch_browsertest.cc",
       "../browser/prerender/prerender_test_utils.cc",
       "../browser/prerender/prerender_test_utils.h",
+      "../browser/previews/lazyload_browsertest.cc",
       "../browser/previews/previews_browsertest.cc",
       "../browser/previews/previews_lite_page_browsertest.cc",
       "../browser/previews/previews_service_browser_test.cc",
diff --git a/chrome/test/chromedriver/logging.cc b/chrome/test/chromedriver/logging.cc
index 96f5e703..94061785 100644
--- a/chrome/test/chromedriver/logging.cc
+++ b/chrome/test/chromedriver/logging.cc
@@ -255,6 +255,7 @@
       return false;
     }
     VLOG(0) << "Starting ChromeDriver " << kChromeDriverVersion;
+    VLOG(0) << kPortProtectionMessage;
   }
 
   Log::truncate_logged_params = !cmd_line->HasSwitch("replayable");
diff --git a/chrome/test/chromedriver/logging.h b/chrome/test/chromedriver/logging.h
index 3f582ab..a13ae5e 100644
--- a/chrome/test/chromedriver/logging.h
+++ b/chrome/test/chromedriver/logging.h
@@ -91,4 +91,8 @@
     std::vector<std::unique_ptr<DevToolsEventListener>>* out_devtools_listeners,
     std::vector<std::unique_ptr<CommandListener>>* out_command_listeners);
 
+const char* const kPortProtectionMessage =
+    "Please protect ports used by ChromeDriver and related test frameworks to "
+    "prevent access by malicious code.";
+
 #endif  // CHROME_TEST_CHROMEDRIVER_LOGGING_H_
diff --git a/chrome/test/chromedriver/server/chromedriver_server.cc b/chrome/test/chromedriver/server/chromedriver_server.cc
index ffa3d15..4cf50ba 100644
--- a/chrome/test/chromedriver/server/chromedriver_server.cc
+++ b/chrome/test/chromedriver/server/chromedriver_server.cc
@@ -42,6 +42,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/url_util.h"
 #include "net/log/net_log_source.h"
 #include "net/server/http_server.h"
 #include "net/server/http_server_request_info.h"
@@ -74,15 +75,39 @@
   return socket->ListenWithAddressAndPort(binding_ip, port, 5);
 }
 
+bool RequestIsSafeToServe(const net::HttpServerRequestInfo& info) {
+  // To guard against browser-originating cross-site requests, when host header
+  // and/or origin header are present, serve only those coming from localhost.
+  std::string host_header = info.headers["host"];
+  if (!host_header.empty()) {
+    GURL url = GURL("http://" + host_header);
+    if (!net::IsLocalhost(url)) {
+      LOG(ERROR) << "Rejecting request with host: " << host_header;
+      return false;
+    }
+  }
+  std::string origin_header = info.headers["origin"];
+  if (!origin_header.empty()) {
+    GURL url = GURL(origin_header);
+    if (!net::IsLocalhost(url)) {
+      LOG(ERROR) << "Rejecting request with origin: " << origin_header;
+      return false;
+    }
+  }
+  return true;
+}
+
 class HttpServer : public net::HttpServer::Delegate {
  public:
   explicit HttpServer(const HttpRequestHandlerFunc& handle_request_func)
       : handle_request_func_(handle_request_func),
+        allow_remote_(false),
         weak_factory_(this) {}
 
   ~HttpServer() override {}
 
   int Start(uint16_t port, bool allow_remote, bool use_ipv4) {
+    allow_remote_ = allow_remote;
     std::unique_ptr<net::ServerSocket> server_socket(
         new net::TCPServerSocket(NULL, net::NetLogSource()));
     int status = use_ipv4
@@ -105,6 +130,13 @@
   }
   void OnHttpRequest(int connection_id,
                      const net::HttpServerRequestInfo& info) override {
+    if (!allow_remote_ && !RequestIsSafeToServe(info)) {
+      server_->Send500(
+          connection_id,
+          "Host header or origin header is specified and is not localhost.",
+          TRAFFIC_ANNOTATION_FOR_TESTS);
+      return;
+    }
     handle_request_func_.Run(
         info,
         base::Bind(&HttpServer::OnResponse,
@@ -132,6 +164,7 @@
 
   HttpRequestHandlerFunc handle_request_func_;
   std::unique_ptr<net::HttpServer> server_;
+  bool allow_remote_;
   base::WeakPtrFactory<HttpServer> weak_factory_;  // Should be last.
 };
 
@@ -465,6 +498,7 @@
     } else {
       printf("All remote connections are allowed. Use a whitelist instead!\n");
     }
+    printf("%s\n", kPortProtectionMessage);
     fflush(stdout);
   }
 
diff --git a/chrome/test/data/lazyload/css-background-image.html b/chrome/test/data/lazyload/css-background-image.html
new file mode 100644
index 0000000..336c596
--- /dev/null
+++ b/chrome/test/data/lazyload/css-background-image.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<style>
+div.css_bg_image {
+  height: 800px;
+  background-color: #cccccc;
+}
+div.a {
+  background-image: url("images/fruit1.jpg");
+}
+div.b {
+  background-image: url("images/fruit2.jpg");
+}
+</style>
+
+<body>
+  <div style="height:10000px;"></div>
+  <a name="images"></a>
+  <div class="css_bg_image a"></div>
+  <div class="css_bg_image b"></div>
+</body>
+</html>
diff --git a/chrome/test/data/lazyload/images/fruit1.jpg b/chrome/test/data/lazyload/images/fruit1.jpg
new file mode 100644
index 0000000..1b7005f
--- /dev/null
+++ b/chrome/test/data/lazyload/images/fruit1.jpg
Binary files differ
diff --git a/chrome/test/data/lazyload/images/fruit2.jpg b/chrome/test/data/lazyload/images/fruit2.jpg
new file mode 100644
index 0000000..1b7005f
--- /dev/null
+++ b/chrome/test/data/lazyload/images/fruit2.jpg
Binary files differ
diff --git a/chrome/test/data/webrtc/webrtc-simulcast.html b/chrome/test/data/webrtc/webrtc-simulcast.html
index f37ffe511..5b91bab 100644
--- a/chrome/test/data/webrtc/webrtc-simulcast.html
+++ b/chrome/test/data/webrtc/webrtc-simulcast.html
@@ -140,7 +140,10 @@
 
 function initialize() {
   trace('Setting up for a new call.');
-  var configs = {iceServers:[]};
+  // TODO(https://crbug.com/921041): Add simulcast test coverage for both Plan B
+  // and Unified Plan as a layout test and remove this browser test which only
+  // currently only works in Plan B.
+  var configs = {iceServers:[], sdpSemantics:'plan-b'};
   var constraints = {'mandatory': {'DtlsSrtpKeyAgreement': false}};
   pcClient = new RTCPeerConnection(configs, constraints);
   trace('Created local peer connection object pcClient');
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 2264d11..e39837a 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -396,9 +396,11 @@
   this.runMochaTest(extension_manager_tests.TestNames.Uninstall);
 });
 
-TEST_F('CrExtensionsManagerUnitTest', 'UninstallFromDetails', function() {
-  this.runMochaTest(extension_manager_tests.TestNames.UninstallFromDetails);
-});
+// Flaky since r621915: https://crbug.com/922490
+TEST_F(
+    'CrExtensionsManagerUnitTest', 'DISABLED_UninstallFromDetails', function() {
+      this.runMochaTest(extension_manager_tests.TestNames.UninstallFromDetails);
+    });
 
 TEST_F('CrExtensionsManagerUnitTest', 'ToggleIncognito', function() {
   this.runMochaTest(extension_manager_tests.TestNames.ToggleIncognitoMode);
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.js b/chrome/test/data/webui/print_preview/destination_select_test.js
index 3e49332a..7bfaa68 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test.js
+++ b/chrome/test/data/webui/print_preview/destination_select_test.js
@@ -80,23 +80,19 @@
      */
     function assertPrinterDisplay(printerName) {
       const destinationSettings = page.$$('print-preview-destination-settings');
+      const destinationSelect = destinationSettings.$.destinationSelect;
 
-      return new Promise(resolve => {
-        Polymer.RenderStatus.afterNextRender(destinationSettings, () => {
-          Polymer.dom.flush();
-          // Check that the throbber is hidden and the dropdown is shown.
-          assertTrue(destinationSettings.$$('.throbber-container').hidden);
-          const destinationSelect = destinationSettings.$.destinationSelect;
-          assertFalse(destinationSelect.hidden);
+      Polymer.dom.flush();
+      return test_util.waitForRender(destinationSelect, () => {
+        // Check that the throbber is hidden and the dropdown is shown.
+        assertTrue(destinationSettings.$$('.throbber-container').hidden);
+        assertFalse(destinationSelect.hidden);
 
-          const options =
-              destinationSelect.shadowRoot.querySelectorAll('option');
-          const selectedOption =
-              options[destinationSelect.$$('.md-select').selectedIndex];
-          // Check that the destination matches the expected destination.
-          assertEquals(printerName, selectedOption.textContent.trim());
-          resolve();
-        });
+        const options = destinationSelect.shadowRoot.querySelectorAll('option');
+        const selectedOption =
+            options[destinationSelect.$$('.md-select').selectedIndex];
+        // Check that the destination matches the expected destination.
+        assertEquals(printerName, selectedOption.textContent.trim());
       });
     }
 
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js
index 6cb2ea0..1691f6c9 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.js
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -64,6 +64,18 @@
       document.body.appendChild(destinationSettings);
     });
 
+    function waitForRender(element) {
+      return new Promise(resolve => {
+        if (Polymer.DomIf) {
+          Polymer.RenderStatus.beforeNextRender(element, resolve);
+        } else {
+          setTimeout(() => {
+            resolve();
+          });
+        }
+      });
+    }
+
     // Tests that the dropdown is enabled or disabled correctly based on
     // the state.
     test(assert(TestNames.ChangeDropdownState), function() {
@@ -84,36 +96,41 @@
       assertTrue(dropdown.disabled);
 
       // Simulate loading a destination and setting state to ready. The dropdown
-      // is still enabled.
+      // is enabled.
       destinationSettings.destination = new print_preview.Destination(
           'FooDevice', print_preview.DestinationType.LOCAL, getLocalOrigin(),
           'FooName', true /* isRecent */,
           print_preview.DestinationConnectionStatus.ONLINE);
+      destinationSettings.recentDestinations = [
+        print_preview.makeRecentDestination(destinationSettings.destination),
+      ];
       destinationSettings.state = print_preview_new.State.READY;
       destinationSettings.disabled = false;
-      assertFalse(dropdown.disabled);
+      return waitForRender(dropdown).then(() => {
+        assertFalse(dropdown.disabled);
 
-      // Simulate setting a setting to an invalid value. Dropdown is disabled
-      // due to validation error on another control.
-      destinationSettings.state = print_preview_new.State.INVALID_TICKET;
-      destinationSettings.disabled = true;
-      assertTrue(dropdown.disabled);
+        // Simulate setting a setting to an invalid value. Dropdown is disabled
+        // due to validation error on another control.
+        destinationSettings.state = print_preview_new.State.INVALID_TICKET;
+        destinationSettings.disabled = true;
+        assertTrue(dropdown.disabled);
 
-      // Simulate the user fixing the validation error, and then selecting an
-      // invalid printer. Dropdown is enabled, so that the user can fix the
-      // error.
-      destinationSettings.state = print_preview_new.State.READY;
-      destinationSettings.disabled = false;
-      destinationSettings.state = print_preview_new.State.INVALID_PRINTER;
-      destinationSettings.disabled = true;
-      assertFalse(dropdown.disabled);
+        // Simulate the user fixing the validation error, and then selecting an
+        // invalid printer. Dropdown is enabled, so that the user can fix the
+        // error.
+        destinationSettings.state = print_preview_new.State.READY;
+        destinationSettings.disabled = false;
+        destinationSettings.state = print_preview_new.State.INVALID_PRINTER;
+        destinationSettings.disabled = true;
+        assertFalse(dropdown.disabled);
 
-      // Simulate the user having no printers.
-      destinationSettings.destination = null;
-      destinationSettings.state = print_preview_new.State.INVALID_PRINTER;
-      destinationSettings.disabled = true;
-      destinationSettings.noDestinationsFound = true;
-      assertTrue(dropdown.disabled);
+        // Simulate the user having no printers.
+        destinationSettings.destination = null;
+        destinationSettings.state = print_preview_new.State.INVALID_PRINTER;
+        destinationSettings.disabled = true;
+        destinationSettings.noDestinationsFound = true;
+        assertTrue(dropdown.disabled);
+      });
     });
 
     /** @return {!print_preview.DestinationOrigin} */
@@ -263,16 +280,16 @@
           .then(() => {
             assertDropdownItems([
               makeLocalDestinationKey('ID1'),
-              'Save as PDF/local/',
               makeLocalDestinationKey('ID3'),
+              'Save as PDF/local/',
             ]);
 
             // If the user is signed in, Save to Drive should be displayed.
             signIn();
             assertDropdownItems([
               makeLocalDestinationKey('ID1'),
-              'Save as PDF/local/',
               makeLocalDestinationKey('ID3'),
+              'Save as PDF/local/',
               '__google__docs/cookies/foo@chromium.org',
             ]);
           });
@@ -307,9 +324,9 @@
             signIn();
             assertDropdownItems([
               makeLocalDestinationKey('ID1'),
-              '__google__docs/cookies/foo@chromium.org',
               makeLocalDestinationKey('ID3'),
               'Save as PDF/local/',
+              '__google__docs/cookies/foo@chromium.org',
             ]);
           });
     });
@@ -336,8 +353,8 @@
           .then(() => {
             assertDropdownItems([
               makeLocalDestinationKey('ID1'),
-              'Save as PDF/local/',
               makeLocalDestinationKey('ID3'),
+              'Save as PDF/local/',
             ]);
             // Most recent destination is selected by default.
             assertEquals('ID1', destinationSettings.destination.id);
@@ -381,9 +398,9 @@
             signIn();
             assertDropdownItems([
               makeLocalDestinationKey('ID1'),
-              '__google__docs/cookies/foo@chromium.org',
               makeLocalDestinationKey('ID3'),
               'Save as PDF/local/',
+              '__google__docs/cookies/foo@chromium.org',
             ]);
             // Most recent destination is selected by default.
             assertEquals('ID1', destinationSettings.destination.id);
diff --git a/chrome/tools/build/mac/build_app_dmg b/chrome/tools/build/mac/build_app_dmg
deleted file mode 100755
index 1ccfcbd7..0000000
--- a/chrome/tools/build/mac/build_app_dmg
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 2012 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.
-
-# Normally you don't want to build a dmg out of a debug build.
-if [ "${CONFIGURATION}" != "Release" ] ; then
-  echo "warning: building dmg in non-release mode" >&2
-fi
-
-# Make sure we got the branding passed to us
-if [ $# -ne 1 ]; then
-  echo "error: missing branding as an argument" >&2
-  exit 1
-fi
-
-# fail on anything from here out
-set -e
-
-TOP="${SRCROOT}/.."
-PKG_DMG="${SRCROOT}/installer/mac/pkg-dmg"
-BRAND_SCRIPT="${TOP}/build/branding_value.sh"
-
-BUILD_BRANDING=$1
-
-APP_NAME=$("${BRAND_SCRIPT}" "${BUILD_BRANDING}" PRODUCT_FULLNAME)
-DMG_NAME=$(echo "${APP_NAME}" | sed "s/ //g")
-
-SRC_APP_PATH="${BUILT_PRODUCTS_DIR}/${APP_NAME}.app"
-VOL_NAME="${APP_NAME}"
-DST_DMG_PATH="${BUILT_PRODUCTS_DIR}/${DMG_NAME}.dmg"
-
-# Call the real working
-"${PKG_DMG}" --source /var/empty \
-             --target "${DST_DMG_PATH}" \
-             --format UDBZ \
-             --verbosity 0 \
-             --volname "${VOL_NAME}" \
-             --tempdir "${TEMP_DIR}" \
-             --copy "${SRC_APP_PATH}/:/${APP_NAME}.app/"
diff --git a/chromecast/app/cast_main_delegate.cc b/chromecast/app/cast_main_delegate.cc
index 80e7e2f1..979a80b1 100644
--- a/chromecast/app/cast_main_delegate.cc
+++ b/chromecast/app/cast_main_delegate.cc
@@ -80,7 +80,7 @@
   if (process_type.empty()) {
     base::FilePath log_file;
     base::PathService::Get(FILE_CAST_ANDROID_LOG, &log_file);
-    settings.logging_dest = logging::LOG_TO_ALL;
+    settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
     settings.log_file = log_file.value().c_str();
     settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   }
diff --git a/chromecast/base/chromecast_switches.cc b/chromecast/base/chromecast_switches.cc
index 4381f4b..f287db6 100644
--- a/chromecast/base/chromecast_switches.cc
+++ b/chromecast/base/chromecast_switches.cc
@@ -45,20 +45,13 @@
 // This flag implies --alsa-check-close-timeout=0.
 const char kAcceptResourceProvider[] = "accept-resource-provider";
 
-// Size of the ALSA output buffer in frames. This directly sets the latency of
-// the output device. Latency can be calculated by multiplying the sample rate
-// by the output buffer size.
-const char kAlsaOutputBufferSize[] = "alsa-output-buffer-size";
+// Name of the device the amp mixer should be opened on. If this flag is not
+// specified it will default to the same device as kAlsaVolumeDeviceName.
+const char kAlsaAmpDeviceName[] = "alsa-amp-device-name";
 
-// Size of the ALSA output period in frames. The period of an ALSA output device
-// determines how many frames elapse between hardware interrupts.
-const char kAlsaOutputPeriodSize[] = "alsa-output-period-size";
-
-// How many frames need to be in the output buffer before output starts.
-const char kAlsaOutputStartThreshold[] = "alsa-output-start-threshold";
-
-// Minimum number of available frames for scheduling a transfer.
-const char kAlsaOutputAvailMin[] = "alsa-output-avail-min";
+// Name of the simple mixer control element that the ALSA-based media library
+// should use to toggle powersave mode on the system.
+const char kAlsaAmpElementName[] = "alsa-amp-element-name";
 
 // Time in ms to wait before closing the PCM handle when no more mixer inputs
 // remain. Assumed to be 0 if --accept-resource-provider is present.
@@ -72,9 +65,28 @@
 // Deprecated: Use --audio-output-sample-rate instead.
 const char kAlsaFixedOutputSampleRate[] = "alsa-fixed-output-sample-rate";
 
+// Name of the device the mute mixer should be opened on. If this flag is not
+// specified it will default to the same device as kAlsaVolumeDeviceName.
+const char kAlsaMuteDeviceName[] = "alsa-mute-device-name";
+
 // Name of the simple mixer control element that the ALSA-based media library
-// should use to control the volume.
-const char kAlsaVolumeElementName[] = "alsa-volume-element-name";
+// should use to mute the system.
+const char kAlsaMuteElementName[] = "alsa-mute-element-name";
+
+// Minimum number of available frames for scheduling a transfer.
+const char kAlsaOutputAvailMin[] = "alsa-output-avail-min";
+
+// Size of the ALSA output buffer in frames. This directly sets the latency of
+// the output device. Latency can be calculated by multiplying the sample rate
+// by the output buffer size.
+const char kAlsaOutputBufferSize[] = "alsa-output-buffer-size";
+
+// Size of the ALSA output period in frames. The period of an ALSA output device
+// determines how many frames elapse between hardware interrupts.
+const char kAlsaOutputPeriodSize[] = "alsa-output-period-size";
+
+// How many frames need to be in the output buffer before output starts.
+const char kAlsaOutputStartThreshold[] = "alsa-output-start-threshold";
 
 // Name of the device the volume control mixer should be opened on. Will use the
 // same device as kAlsaOutputDevice and fall back to "default" if
@@ -82,23 +94,8 @@
 const char kAlsaVolumeDeviceName[] = "alsa-volume-device-name";
 
 // Name of the simple mixer control element that the ALSA-based media library
-// should use to mute the system.
-const char kAlsaMuteElementName[] = "alsa-mute-element-name";
-
-// Name of the device the mute mixer should be opened on. If this flag is not
-// specified it will default to the same device as kAlsaVolumeDeviceName.
-const char kAlsaMuteDeviceName[] = "alsa-mute-device-name";
-
-// Name of the simple mixer control element that the ALSA-based media library
-// should use to toggle powersave mode on the system.
-const char kAlsaAmpElementName[] = "alsa-amp-element-name";
-
-// Name of the device the amp mixer should be opened on. If this flag is not
-// specified it will default to the same device as kAlsaVolumeDeviceName.
-const char kAlsaAmpDeviceName[] = "alsa-amp-device-name";
-
-// Calibrated max output volume dBa for voice content at 1 meter, if known.
-const char kMaxOutputVolumeDba1m[] = "max-output-volume-dba1m";
+// should use to control the volume.
+const char kAlsaVolumeElementName[] = "alsa-volume-element-name";
 
 // Number of audio output channels. This will be used to send audio buffer with
 // specific number of channels to ALSA and generate loopback audio. Default
@@ -110,6 +107,21 @@
 // the media stream.
 const char kAudioOutputSampleRate[] = "audio-output-sample-rate";
 
+// Calibrated max output volume dBa for voice content at 1 meter, if known.
+const char kMaxOutputVolumeDba1m[] = "max-output-volume-dba1m";
+
+// Specify the start threshold frames for audio output when using our mixer.
+// This is mostly used to override the default value to a larger value, for
+// platforms that can't handle the default start threshold without running into
+// audio underruns.
+const char kMixerSourceAudioReadyThresholdMs[] =
+    "mixer-source-audio-ready-threshold-ms";
+
+// Specify the buffer size for audio output when using our mixer. This is mostly
+// used to override the default value to a larger value, for platforms that
+// can't handle an audio buffer so small without running into audio underruns.
+const char kMixerSourceInputQueueMs[] = "mixer-source-input-queue-ms";
+
 // Some platforms typically have very little 'free' memory, but plenty is
 // available in buffers+cached.  For such platforms, configure this amount
 // as the portion of buffers+cached memory that should be treated as
diff --git a/chromecast/base/chromecast_switches.h b/chromecast/base/chromecast_switches.h
index 77823e0..456ab1b7 100644
--- a/chromecast/base/chromecast_switches.h
+++ b/chromecast/base/chromecast_switches.h
@@ -42,22 +42,24 @@
 // TODO(sergeyu): kAlsaEnableUpsampling and kAlsaCheckCloseTimeout are
 // implemented in StreamMixer, which is not ALSA-specific - it's also used on
 // Fuchsia. Rename these flags.
-extern const char kAlsaOutputBufferSize[];
-extern const char kAlsaOutputPeriodSize[];
-extern const char kAlsaOutputStartThreshold[];
-extern const char kAlsaOutputAvailMin[];
+extern const char kAlsaAmpDeviceName[];
+extern const char kAlsaAmpElementName[];
 extern const char kAlsaCheckCloseTimeout[];
 extern const char kAlsaEnableUpsampling[];
 extern const char kAlsaFixedOutputSampleRate[];
-extern const char kAlsaVolumeDeviceName[];
-extern const char kAlsaVolumeElementName[];
 extern const char kAlsaMuteDeviceName[];
 extern const char kAlsaMuteElementName[];
-extern const char kAlsaAmpDeviceName[];
-extern const char kAlsaAmpElementName[];
-extern const char kMaxOutputVolumeDba1m[];
+extern const char kAlsaOutputAvailMin[];
+extern const char kAlsaOutputBufferSize[];
+extern const char kAlsaOutputPeriodSize[];
+extern const char kAlsaOutputStartThreshold[];
+extern const char kAlsaVolumeDeviceName[];
+extern const char kAlsaVolumeElementName[];
 extern const char kAudioOutputChannels[];
 extern const char kAudioOutputSampleRate[];
+extern const char kMaxOutputVolumeDba1m[];
+extern const char kMixerSourceAudioReadyThresholdMs[];
+extern const char kMixerSourceInputQueueMs[];
 
 // Memory pressure switches
 extern const char kMemPressureSystemReservedKb[];
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 122f243a..9862560b 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -253,6 +253,7 @@
       "extensions/api/accessibility_private/accessibility_extension_api.h",
       "extensions/api/automation_internal/automation_event_router.cc",
       "extensions/api/automation_internal/automation_event_router.h",
+      "extensions/api/automation_internal/automation_event_router_interface.h",
       "extensions/api/automation_internal/automation_internal_api.cc",
       "extensions/api/automation_internal/automation_internal_api.h",
       "extensions/api/bookmarks/bookmarks_api.cc",
diff --git a/chromecast/browser/extensions/api/automation_internal/automation_event_router.cc b/chromecast/browser/extensions/api/automation_internal/automation_event_router.cc
index 09dceebf..e69fdbb 100644
--- a/chromecast/browser/extensions/api/automation_internal/automation_event_router.cc
+++ b/chromecast/browser/extensions/api/automation_internal/automation_event_router.cc
@@ -56,6 +56,26 @@
   Register(extension_id, listener_process_id, ui::AXTreeIDUnknown(), true);
 }
 
+void AutomationEventRouter::DispatchActionResult(
+    const ui::AXActionData& data,
+    bool result,
+    content::BrowserContext* active_profile) {
+  CHECK(!data.source_extension_id.empty());
+
+  if (listeners_.empty())
+    return;
+
+  std::unique_ptr<base::ListValue> args(
+      api::automation_internal::OnActionResult::Create(
+          data.target_tree_id.ToString(), data.request_id, result));
+  auto event = std::make_unique<Event>(
+      events::AUTOMATION_INTERNAL_ON_ACTION_RESULT,
+      api::automation_internal::OnActionResult::kEventName, std::move(args),
+      active_profile);
+  EventRouter::Get(active_profile)
+      ->DispatchEventToExtension(data.source_extension_id, std::move(event));
+}
+
 void AutomationEventRouter::DispatchAccessibilityEvents(
     const ExtensionMsg_AccessibilityEventBundleParams& event_bundle) {
   for (const auto& listener : listeners_) {
diff --git a/chromecast/browser/extensions/api/automation_internal/automation_event_router.h b/chromecast/browser/extensions/api/automation_internal/automation_event_router.h
index 4792af3..6d6f4e88 100644
--- a/chromecast/browser/extensions/api/automation_internal/automation_event_router.h
+++ b/chromecast/browser/extensions/api/automation_internal/automation_event_router.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/memory/singleton.h"
+#include "chromecast/browser/extensions/api/automation_internal/automation_event_router_interface.h"
 #include "chromecast/common/extensions_api/automation_internal.h"
 #include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/notification_observer.h"
@@ -32,7 +33,8 @@
 namespace cast {
 struct AutomationListener;
 
-class AutomationEventRouter : public content::NotificationObserver {
+class AutomationEventRouter : public AutomationEventRouterInterface,
+                              content::NotificationObserver {
  public:
   static AutomationEventRouter* GetInstance();
 
@@ -51,15 +53,21 @@
                                              int listener_process_id);
 
   void DispatchAccessibilityEvents(
-      const ExtensionMsg_AccessibilityEventBundleParams& events);
+      const ExtensionMsg_AccessibilityEventBundleParams& events) override;
 
   void DispatchAccessibilityLocationChange(
-      const ExtensionMsg_AccessibilityLocationChangeParams& params);
+      const ExtensionMsg_AccessibilityLocationChangeParams& params) override;
 
   // Notify all automation extensions that an accessibility tree was
   // destroyed. If |browser_context| is null,
-  void DispatchTreeDestroyedEvent(ui::AXTreeID tree_id,
-                                  content::BrowserContext* browser_context);
+  void DispatchTreeDestroyedEvent(
+      ui::AXTreeID tree_id,
+      content::BrowserContext* browser_context) override;
+
+  // Notify the source extension of the action of an action result.
+  void DispatchActionResult(const ui::AXActionData& data,
+                            bool result,
+                            content::BrowserContext* active_profile) override;
 
  private:
   struct AutomationListener {
diff --git a/chromecast/browser/extensions/api/automation_internal/automation_event_router_interface.h b/chromecast/browser/extensions/api/automation_internal/automation_event_router_interface.h
new file mode 100644
index 0000000..fc8954754
--- /dev/null
+++ b/chromecast/browser/extensions/api/automation_internal/automation_event_router_interface.h
@@ -0,0 +1,62 @@
+// 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 CHROMECAST_BROWSER_EXTENSIONS_API_AUTOMATION_INTERNAL_AUTOMATION_EVENT_ROUTER_INTERFACE_H_
+#define CHROMECAST_BROWSER_EXTENSIONS_API_AUTOMATION_INTERNAL_AUTOMATION_EVENT_ROUTER_INTERFACE_H_
+
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "chromecast/common/extensions_api/automation_internal.h"
+#include "content/public/browser/ax_event_notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "extensions/common/extension_id.h"
+#include "extensions/common/extension_messages.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace ui {
+struct AXActionData;
+}  // namespace ui
+
+struct ExtensionMsg_AccessibilityEventBundleParams;
+struct ExtensionMsg_AccessibilityLocationChangeParams;
+
+namespace extensions {
+namespace cast {
+
+class AutomationEventRouterInterface {
+ public:
+  virtual void DispatchAccessibilityEvents(
+      const ExtensionMsg_AccessibilityEventBundleParams& events) = 0;
+
+  virtual void DispatchAccessibilityLocationChange(
+      const ExtensionMsg_AccessibilityLocationChangeParams& params) = 0;
+
+  // Notify all automation extensions that an accessibility tree was
+  // destroyed. If |browser_context| is null,
+  virtual void DispatchTreeDestroyedEvent(
+      ui::AXTreeID tree_id,
+      content::BrowserContext* browser_context) = 0;
+
+  // Notify the source extension of the action of an action result.
+  virtual void DispatchActionResult(
+      const ui::AXActionData& data,
+      bool result,
+      content::BrowserContext* active_profile) = 0;
+
+  AutomationEventRouterInterface(){};
+  virtual ~AutomationEventRouterInterface(){};
+
+  DISALLOW_COPY_AND_ASSIGN(AutomationEventRouterInterface);
+};
+
+}  // namespace cast
+}  // namespace extensions
+
+#endif  // CHROMECAST_BROWSER_EXTENSIONS_API_AUTOMATION_INTERNAL_AUTOMATION_EVENT_ROUTER_INTERFACE_H_
diff --git a/chromecast/media/cma/backend/buffering_mixer_source.cc b/chromecast/media/cma/backend/buffering_mixer_source.cc
index 63ec861..f30572e8 100644
--- a/chromecast/media/cma/backend/buffering_mixer_source.cc
+++ b/chromecast/media/cma/backend/buffering_mixer_source.cc
@@ -11,10 +11,12 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "chromecast/base/chromecast_switches.h"
 #include "chromecast/media/cma/backend/stream_mixer.h"
 #include "chromecast/media/cma/base/decoder_buffer_adapter.h"
 #include "chromecast/media/cma/base/decoder_buffer_base.h"
@@ -35,9 +37,9 @@
 namespace {
 
 const int kNumOutputChannels = 2;
-const int64_t kInputQueueMs = 90;
+const int64_t kDefaultInputQueueMs = 90;
 const int kFadeTimeMs = 5;
-const int kAudioReadyForPlaybackThresholdMs = 70;
+const int kDefaultAudioReadyForPlaybackThresholdMs = 70;
 
 // Special queue size and start threshold for "communications" streams to avoid
 // issues with voice calling.
@@ -71,17 +73,37 @@
 }
 
 int MaxQueuedFrames(const std::string& device_id, int sample_rate) {
+  int64_t queue_ms = kDefaultInputQueueMs;
+
   if (device_id == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
-    return MsToSamples(kCommsInputQueueMs, sample_rate);
+    queue_ms = kCommsInputQueueMs;
   }
-  return MsToSamples(kInputQueueMs, sample_rate);
+
+  if (base::CommandLine::InitializedForCurrentProcess() &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kMixerSourceInputQueueMs)) {
+    queue_ms = GetSwitchValueNonNegativeInt(switches::kMixerSourceInputQueueMs,
+                                            queue_ms);
+  }
+
+  return MsToSamples(queue_ms, sample_rate);
 }
 
 int StartThreshold(const std::string& device_id, int sample_rate) {
+  int64_t start_threshold_ms = kDefaultAudioReadyForPlaybackThresholdMs;
+
   if (device_id == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
-    return MsToSamples(kCommsStartThresholdMs, sample_rate);
+    start_threshold_ms = kCommsStartThresholdMs;
   }
-  return MsToSamples(kAudioReadyForPlaybackThresholdMs, sample_rate);
+
+  if (base::CommandLine::InitializedForCurrentProcess() &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kMixerSourceAudioReadyThresholdMs)) {
+    start_threshold_ms = GetSwitchValueNonNegativeInt(
+        switches::kMixerSourceAudioReadyThresholdMs, start_threshold_ms);
+  }
+
+  return MsToSamples(start_threshold_ms, sample_rate);
 }
 
 }  // namespace
@@ -301,7 +323,8 @@
                     (data->timestamp() - playback_start_pts_) < 100000 &&
                     locked->playback_start_timestamp_ != INT64_MIN))
           << "Queueing pts diff=" << data->timestamp() - playback_start_pts_
-          << " current buffered data=" << GetCurrentBufferedDataInUs() / 1000;
+          << " locked->queued_frames_=" << locked->queued_frames_
+          << " start_threshold_frames_=" << start_threshold_frames_;
 
       scoped_refptr<DecoderBufferBase> buffer =
           locked->audio_resampler_.ResampleBuffer(std::move(data));
@@ -314,8 +337,7 @@
       // POST_TASK_TO_CALLER_THREAD should also probably DCHECK that the lock
       // is not held before executing.
       if (!locked->started_ &&
-          GetCurrentBufferedDataInUs() >=
-              kAudioReadyForPlaybackThresholdMs * 1000 &&
+          locked->queued_frames_ >= start_threshold_frames_ &&
           locked->playback_start_timestamp_ != INT64_MIN) {
         POST_TASK_TO_CALLER_THREAD(PostAudioReadyForPlayback);
       }
@@ -531,11 +553,9 @@
 }
 
 bool BufferingMixerSource::CanDropFrames(int64_t frames_to_drop) {
-  int64_t duration_of_frames_us =
-      SamplesToMicroseconds(frames_to_drop, input_samples_per_second_);
-
-  return (GetCurrentBufferedDataInUs() - duration_of_frames_us) >=
-         (kAudioReadyForPlaybackThresholdMs * 1000);
+  auto locked = locked_members_.AssertAcquired();
+  return locked->queued_frames_ - frames_to_drop >=
+         start_threshold_frames_;
 }
 
 int64_t BufferingMixerSource::DataToFrames(int64_t size_in_bytes) {
@@ -695,19 +715,5 @@
   delete this;
 }
 
-int64_t BufferingMixerSource::GetCurrentBufferedDataInUs() {
-  auto locked = locked_members_.AssertAcquired();
-
-  int64_t buffered_data = 0;
-  for (auto buffer : locked->queue_) {
-    buffered_data += buffer->data_size();
-  }
-
-  int buffered_frames = DataToFrames(buffered_data);
-  buffered_frames -= locked->current_buffer_offset_;
-
-  return SamplesToMicroseconds(buffered_frames, input_samples_per_second_);
-}
-
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/cma/backend/buffering_mixer_source.h b/chromecast/media/cma/backend/buffering_mixer_source.h
index 2ccbe0a7..d5da6d10 100644
--- a/chromecast/media/cma/backend/buffering_mixer_source.h
+++ b/chromecast/media/cma/backend/buffering_mixer_source.h
@@ -227,7 +227,6 @@
   void PostEos();
   void PostError(MixerError error);
   void PostAudioReadyForPlayback();
-  int64_t GetCurrentBufferedDataInUs();
   void DropAudio(int64_t frames);
   bool CanDropFrames(int64_t frames_to_drop);
   int64_t DataToFrames(int64_t size);
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 9f35e89c..ae3a38c 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -126,6 +126,7 @@
                                         bool enable_hotword,
                                         base::OnceClosure post_init_callback) {
   DCHECK(!assistant_manager_);
+  DCHECK_EQ(state_, State::STOPPED);
 
   // Set the flag to avoid starting the service multiple times.
   state_ = State::STARTED;
@@ -159,6 +160,9 @@
 }
 
 void AssistantManagerServiceImpl::Stop() {
+  // We cannot cleanly stop the service if it is in the process of starting up.
+  DCHECK_NE(state_, State::STARTED);
+
   state_ = State::STOPPED;
 
   assistant_manager_internal_ = nullptr;
@@ -774,6 +778,7 @@
     base::OnceClosure post_init_callback,
     std::unique_ptr<assistant_client::AssistantManager>* assistant_manager) {
   DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence());
+  DCHECK_EQ(state_, State::STARTED);
 
   assistant_manager_ = std::move(*assistant_manager);
   assistant_manager_internal_ =
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc
index 40fb63e6..a95bc13d 100644
--- a/chromeos/services/assistant/service.cc
+++ b/chromeos/services/assistant/service.cc
@@ -153,20 +153,33 @@
 }
 
 void Service::OnVoiceInteractionHotwordEnabled(bool enabled) {
-  if (assistant_manager_service_ &&
-      assistant_manager_service_->GetState() !=
-          AssistantManagerService::State::STOPPED) {
-    // Hotword status change requires restarting assistant manager.
-    StopAssistantManagerService();
-  }
-
-  UpdateAssistantManagerState();
+  // Hotword status change requires restarting assistant manager.
+  MaybeRestartAssistantManager();
 }
 
 void Service::OnLocaleChanged(const std::string& locale) {
   UpdateAssistantManagerState();
 }
 
+void Service::MaybeRestartAssistantManager() {
+  if (assistant_manager_service_) {
+    switch (assistant_manager_service_->GetState()) {
+      case AssistantManagerService::State::RUNNING:
+        StopAssistantManagerService();
+        break;
+      case AssistantManagerService::State::STARTED:
+        // A previous instance of assistant manager is still in the process
+        // of starting. We need to wait for that to finish before trying to
+        // restart a new one to avoid potentially multiple instances running.
+        pending_restart_assistant_manager_ = true;
+        return;
+      case AssistantManagerService::State::STOPPED:
+        break;
+    }
+  }
+  UpdateAssistantManagerState();
+}
+
 void Service::UpdateAssistantManagerState() {
   if (!assistant_state_.hotword_enabled().has_value() ||
       !assistant_state_.settings_enabled().has_value() ||
@@ -331,6 +344,12 @@
   client_->OnAssistantStatusChanged(true /* running */);
   UpdateListeningState();
   DVLOG(1) << "Assistant is running";
+
+  if (pending_restart_assistant_manager_) {
+    pending_restart_assistant_manager_ = false;
+    StopAssistantManagerService();
+    UpdateAssistantManagerState();
+  }
 }
 
 void Service::StopAssistantManagerService() {
diff --git a/chromeos/services/assistant/service.h b/chromeos/services/assistant/service.h
index c4b772f..ab3419f 100644
--- a/chromeos/services/assistant/service.h
+++ b/chromeos/services/assistant/service.h
@@ -125,6 +125,7 @@
   void OnVoiceInteractionHotwordEnabled(bool enabled) override;
   void OnLocaleChanged(const std::string& locale) override;
 
+  void MaybeRestartAssistantManager();
   void UpdateAssistantManagerState();
   void BindAssistantSettingsManager(
       mojom::AssistantSettingsManagerRequest request);
@@ -179,6 +180,8 @@
   bool session_active_ = false;
   // Whether the lock screen is on.
   bool locked_ = false;
+  // Whether there is a pending run for updating AssistantManagerService
+  bool pending_restart_assistant_manager_ = false;
 
   base::Optional<std::string> access_token_;
 
diff --git a/components/download/public/common/auto_resumption_handler.cc b/components/download/public/common/auto_resumption_handler.cc
index 3f1c6f6..fdd6eb4 100644
--- a/components/download/public/common/auto_resumption_handler.cc
+++ b/components/download/public/common/auto_resumption_handler.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -93,7 +92,9 @@
 
 namespace download {
 
-AutoResumptionHandler::Config::Config() : auto_resumption_size_limit(0) {}
+AutoResumptionHandler::Config::Config()
+    : auto_resumption_size_limit(0),
+      is_auto_resumption_enabled_in_native(false) {}
 
 // static
 void AutoResumptionHandler::Create(
@@ -225,6 +226,9 @@
 }
 
 void AutoResumptionHandler::RescheduleTaskIfNecessary() {
+  if (!config_->is_auto_resumption_enabled_in_native)
+    return;
+
   recompute_task_params_scheduled_ = false;
 
   bool has_resumable_downloads = false;
@@ -259,6 +263,9 @@
 }
 
 void AutoResumptionHandler::ResumePendingDownloads() {
+  if (!config_->is_auto_resumption_enabled_in_native)
+    return;
+
   for (auto iter = resumable_downloads_.begin();
        iter != resumable_downloads_.end(); ++iter) {
     download::DownloadItem* download = iter->second;
diff --git a/components/download/public/common/auto_resumption_handler.h b/components/download/public/common/auto_resumption_handler.h
index 2feac61..a66c8c6a 100644
--- a/components/download/public/common/auto_resumption_handler.h
+++ b/components/download/public/common/auto_resumption_handler.h
@@ -31,6 +31,7 @@
     ~Config() = default;
 
     int auto_resumption_size_limit;
+    bool is_auto_resumption_enabled_in_native;
   };
 
   // Creates the singleton instance of AutoResumptionHandler.
diff --git a/components/download/public/common/auto_resumption_handler_unittest.cc b/components/download/public/common/auto_resumption_handler_unittest.cc
index 105f37b..f728dd5 100644
--- a/components/download/public/common/auto_resumption_handler_unittest.cc
+++ b/components/download/public/common/auto_resumption_handler_unittest.cc
@@ -42,6 +42,7 @@
 
     auto config = std::make_unique<AutoResumptionHandler::Config>();
     config->auto_resumption_size_limit = 100;
+    config->is_auto_resumption_enabled_in_native = true;
 
     auto_resumption_handler_ = std::make_unique<AutoResumptionHandler>(
         std::move(network_listener), std::move(task_manager),
diff --git a/components/download/public/common/download_features.cc b/components/download/public/common/download_features.cc
index 26b0a2b..cf01b32c 100644
--- a/components/download/public/common/download_features.cc
+++ b/components/download/public/common/download_features.cc
@@ -9,6 +9,15 @@
 namespace download {
 namespace features {
 
+const base::Feature kDownloadAutoResumptionNative {
+  "DownloadsAutoResumptionNative",
+#if defined(OS_ANDROID)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
 const base::Feature kParallelDownloading {
   "ParallelDownloading",
 #if defined(OS_ANDROID)
diff --git a/components/download/public/common/download_features.h b/components/download/public/common/download_features.h
index 6f08406..9b6b8943 100644
--- a/components/download/public/common/download_features.h
+++ b/components/download/public/common/download_features.h
@@ -11,6 +11,10 @@
 namespace download {
 namespace features {
 
+// Whether download auto-resumptions are enabled in native.
+COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature
+    kDownloadAutoResumptionNative;
+
 // Whether a download can be handled by parallel jobs.
 COMPONENTS_DOWNLOAD_EXPORT extern const base::Feature kParallelDownloading;
 
diff --git a/components/gwp_asan/BUILD.gn b/components/gwp_asan/BUILD.gn
index 5176c8c..48db66c 100644
--- a/components/gwp_asan/BUILD.gn
+++ b/components/gwp_asan/BUILD.gn
@@ -7,7 +7,7 @@
   deps = [
     "//components/gwp_asan/common:unit_tests",
   ]
-  if (is_win || is_mac) {
+  if (is_win) {
     deps += [
       "//components/gwp_asan/client:unit_tests",
       "//components/gwp_asan/crash_handler:unit_tests",
diff --git a/components/gwp_asan/client/BUILD.gn b/components/gwp_asan/client/BUILD.gn
index 071d995..1726f2d 100644
--- a/components/gwp_asan/client/BUILD.gn
+++ b/components/gwp_asan/client/BUILD.gn
@@ -15,14 +15,10 @@
     "gwp_asan.h",
     "sampling_allocator_shims.cc",
     "sampling_allocator_shims.h",
-    "sampling_allocator_shims_win.h",
   ]
 
   if (is_posix) {
-    sources += [
-      "guarded_page_allocator_posix.cc",
-      "sampling_allocator_shims_posix.h",
-    ]
+    sources += [ "guarded_page_allocator_posix.cc" ]
   }
 
   defines = [ "GWP_ASAN_IMPLEMENTATION" ]
diff --git a/components/gwp_asan/client/sampling_allocator_shims.cc b/components/gwp_asan/client/sampling_allocator_shims.cc
index 26cbc4bd..07c59be 100644
--- a/components/gwp_asan/client/sampling_allocator_shims.cc
+++ b/components/gwp_asan/client/sampling_allocator_shims.cc
@@ -17,12 +17,11 @@
 #include "components/gwp_asan/client/export.h"
 #include "components/gwp_asan/client/guarded_page_allocator.h"
 
-#if defined(OS_POSIX)
-#include "components/gwp_asan/client/sampling_allocator_shims_posix.h"
-#endif
-
-#if defined(OS_WIN)
-#include "components/gwp_asan/client/sampling_allocator_shims_win.h"
+#if defined(OS_MACOSX)
+// TODO(https://crbug.com/829078): thread_local is not currently supported on
+// macOS; however, it works correctly on other platforms and is noticeably
+// faster.
+#error "macOS does not support thread_local"
 #endif
 
 namespace gwp_asan {
@@ -39,25 +38,24 @@
   constexpr SamplingState() {}
 
   void Init(size_t sampling_frequency) {
-    TLSInit(&tls_key_);
-
     DCHECK_GT(sampling_frequency, 0U);
     sampling_frequency_ = sampling_frequency;
   }
 
   // Return true if this allocation should be sampled.
-  bool Sample() {
+  ALWAYS_INLINE bool Sample() {
     // For a new thread the initial TLS value will be zero, we do not want to
     // sample on zero as it will always sample the first allocation on thread
     // creation and heavily bias allocations towards that particular call site.
     //
     // Instead, use zero to mean 'get a new counter value' and one to mean
     // that this allocation should be sampled.
-    size_t samples_left = TLSGetValue(tls_key_);
+    static thread_local size_t tls_counter = 0;
+    size_t samples_left = tls_counter;
     if (UNLIKELY(!samples_left))
       samples_left = NextSample();
 
-    TLSSetValue(tls_key_, samples_left - 1);
+    tls_counter = samples_left - 1;
     return (samples_left == 1);
   }
 
@@ -74,7 +72,6 @@
     return next_sample;
   }
 
-  TLSKey tls_key_ = 0;
   size_t sampling_frequency_ = 0;
 
   // Stores the number of allocations we need to skip to reach the end of the
diff --git a/components/gwp_asan/client/sampling_allocator_shims_posix.h b/components/gwp_asan/client/sampling_allocator_shims_posix.h
deleted file mode 100644
index b85833e89..0000000
--- a/components/gwp_asan/client/sampling_allocator_shims_posix.h
+++ /dev/null
@@ -1,40 +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.
-
-#ifndef COMPONENTS_GWP_ASAN_CLIENT_SAMPLING_ALLOCATOR_SHIMS_POSIX_H_
-#define COMPONENTS_GWP_ASAN_CLIENT_SAMPLING_ALLOCATOR_SHIMS_POSIX_H_
-
-#include <pthread.h>
-#include <stddef.h>
-
-#include "base/logging.h"
-
-namespace gwp_asan {
-namespace internal {
-
-// Due to https://crbug.com/881352 the sampling allocator shims can not use
-// base::ThreadLocalStorage to access the TLS. Instead, we use platform-specific
-// TLS APIs.
-//
-// TODO(vtsyrklevich): This implementation is identical to code in the
-// base::PoissonAllocationSampler, see if they can share code.
-using TLSKey = pthread_key_t;
-
-void TLSInit(TLSKey* key) {
-  int result = pthread_key_create(key, nullptr);
-  CHECK_EQ(0, result);
-}
-
-size_t TLSGetValue(const TLSKey& key) {
-  return reinterpret_cast<size_t>(pthread_getspecific(key));
-}
-
-void TLSSetValue(const TLSKey& key, size_t value) {
-  pthread_setspecific(key, reinterpret_cast<void*>(value));
-}
-
-}  // namespace internal
-}  // namespace gwp_asan
-
-#endif  // COMPONENTS_GWP_ASAN_CLIENT_SAMPLING_ALLOCATOR_SHIMS_POSIX_H_
diff --git a/components/gwp_asan/client/sampling_allocator_shims_win.h b/components/gwp_asan/client/sampling_allocator_shims_win.h
deleted file mode 100644
index a67664fd..0000000
--- a/components/gwp_asan/client/sampling_allocator_shims_win.h
+++ /dev/null
@@ -1,46 +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.
-
-#ifndef COMPONENTS_GWP_ASAN_CLIENT_SAMPLING_ALLOCATOR_SHIMS_WIN_H_
-#define COMPONENTS_GWP_ASAN_CLIENT_SAMPLING_ALLOCATOR_SHIMS_WIN_H_
-
-#include <stddef.h>
-#include <windows.h>
-
-#include "base/logging.h"
-
-namespace gwp_asan {
-namespace internal {
-
-// Due to https://crbug.com/881352 the sampling allocator shims can not use
-// base::ThreadLocalStorage to access the TLS. Instead, we use platform-specific
-// TLS APIs.
-//
-// TODO(vtsyrklevich): This implementation is identical to code in the
-// base::PoissonAllocationSampler, see if they can share code.
-using TLSKey = DWORD;
-
-void TLSInit(TLSKey* key) {
-  *key = ::TlsAlloc();
-  CHECK_NE(TLS_OUT_OF_INDEXES, *key);
-}
-
-size_t TLSGetValue(const TLSKey& key) {
-  // TlsGetValue() sets GetLastError() to ERROR_SUCCESS to differentiate between
-  // success and failure since any return value is a possible valid result.
-  // Preserve it in case it was already set.
-  int last_error = ::GetLastError();
-  size_t retval = reinterpret_cast<size_t>(::TlsGetValue(key));
-  ::SetLastError(last_error);
-  return retval;
-}
-
-void TLSSetValue(const TLSKey& key, size_t value) {
-  ::TlsSetValue(key, reinterpret_cast<LPVOID>(value));
-}
-
-}  // namespace internal
-}  // namespace gwp_asan
-
-#endif  // COMPONENTS_GWP_ASAN_CLIENT_SAMPLING_ALLOCATOR_SHIMS_WIN_H_
diff --git a/components/password_manager/core/browser/import/csv_reader.cc b/components/password_manager/core/browser/import/csv_reader.cc
index 706c91f6..1e01bf10 100644
--- a/components/password_manager/core/browser/import/csv_reader.cc
+++ b/components/password_manager/core/browser/import/csv_reader.cc
@@ -12,87 +12,275 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/strings/string_util.h"
-#include "third_party/re2/src/re2/re2.h"
 
 namespace {
 
-// Regular expression that matches and captures the first row in CSV formatted
-// data (i.e., until the first newline that is not enclosed in double quotes).
-// Will throw away the potential trailing EOL character (which is expected to
-// have already been normalized to a single '\n').
-const char kFirstRowRE[] =
-    // Match and capture sequences of 1.) arbitrary characters inside correctly
-    // matched double-quotes, or 2.) characters other than the double quote and
-    // EOL. Note that because literal double-quotes are escaped as two double
-    // quotes and are always enclosed in double quotes, they do not need special
-    // treatment as far as splitting on EOL is concerned. However, this RE will
-    // still accept inputs such as: "a"b"c"\n.
-    "^((?:\"[^\"]*\"|[^\"\\n])*)"
-    // Match and throw away EOL, or match end-of-string.
-    "(?:\n|$)";
+// Returns all the characters from the start of |input| until the first '\n',
+// '\r' (exclusive) or the end of |input|. Cuts the returned part (inclusive the
+// line breaks) from |input|. Skips blocks of matching quotes. Examples:
+// old input -> returned value, new input
+// "ab\ncd" -> "ab", "cd"
+// "\r\n" -> "", "\n"
+// "abcd" -> "abcd", ""
+// "a\"\n\"b" -> "a\"\n\"b", ""
+base::StringPiece ConsumeLine(base::StringPiece* input) {
+  DCHECK(input);
+  DCHECK(!input->empty());
 
-// Regular expression that matches and captures the value of the first field in
-// a CSV formatted row of data. Will throw away the potential trailing comma,
-// but not the enclosing double quotes if the value is quoted.
-const char kFirstFieldRE[] =
-    // Match and capture sequences of 1.) arbitrary characters inside correctly
-    // matched double-quotes, or 2.) characters other than the double quote and
-    // the field separator comma (,). We do not allow a mix of both kinds so as
-    // to reject inputs like: "a"b"c".
-    "^((?:\"[^\"]*\")*|[^\",]*)"
-    // Match and throw away the field separator, or match end-of-string.
-    "(?:,|$)";
+  bool inside_quotes = false;
+  for (size_t current = 0; current < input->size(); ++current) {
+    switch ((*input)[current]) {
+      case '\n':
+      case '\r':
+        if (!inside_quotes) {
+          base::StringPiece ret(input->data(), current);
+          *input = input->substr(current + 1);
+          return ret;
+        }
+        break;
+      case '"':
+        inside_quotes = !inside_quotes;
+        break;
+      default:
+        break;
+    }
+  }
 
-// Encapsulates the pre-compiled regular expressions and provides the logic to
-// parse fields from a CSV file row by row.
+  // The whole |*input| is one line.
+  base::StringPiece ret = *input;
+  *input = base::StringPiece();
+  return ret;
+}
+
+// Created for a row (line) of comma-separated-values, iteratively returns
+// individual fields.
+class FieldParser {
+ public:
+  explicit FieldParser(base::StringPiece row);
+  ~FieldParser();
+
+  // Advances the parser over the next comma-separated field and writes its
+  // contents into |field_contents| (comma separator excluded, enclosing
+  // quotation marks excluded, if present). Returns true if there were no
+  // errors. The input must not be empty (check with HasMoreFields() before
+  // calling).
+  // TODO(crbug.com/918530): Also unescape the field contents.
+  bool NextField(base::StringPiece* field_contents);
+
+  bool HasMoreFields() const {
+    return state_ != State::kError && position_ <= row_.size();
+  }
+
+ private:
+  enum class State {
+    // The state just before a new field begins.
+    kInit,
+    // The state after parsing a syntax error.
+    kError,
+    // When inside a non-escaped block.
+    kPlain,
+    // When inside a quotation-mark-escaped block.
+    kQuoted,
+    // When after reading a block starting and ending with quotation maks. For
+    // the following input, the state would be visited after reading characters
+    // 4 and 7:
+    // a,"b""c",d
+    // 0123456789
+    kAfter,
+  };
+
+  // Returns the next character to be read and updates |position_|.
+  char ConsumeChar();
+
+  // Updates |state_| based on the next character to be read, according to this
+  // diagram (made with help of asciiflow.com):
+  //
+  //   ,
+  //  +--+  +--------------------------+
+  //  |  |  |                          |
+  // +V--+--V+all but " or , +--------+|
+  // |       +--------------->        ||
+  // | kInit |               | kPlain ||
+  // |       <---------------+        ||
+  // ++------+      ,        +^------++|
+  //  |                       |      | |
+  // "|                       +------+ |
+  //  |                    all but ,   |,
+  //  |                                |
+  //  |                                |
+  //  |   +---------+    "     +-------++
+  //  |   |         +---------->        |
+  //  +---> kQuoted |          | kAfter |
+  //      |         <----------+        |
+  //      +---------+    "     +-----+--+
+  //                                |
+  //      +--------+                |
+  //      |        |                |
+  //      | kError <----------------+
+  //      |        |   all but " or ,
+  //      +--------+
+  //
+  // The state kError has no outgoing transitions and so UpdateState should not
+  // be called when this state has been entered.
+  void UpdateState();
+
+  // State of the parser.
+  State state_ = State::kInit;
+  // The input.
+  const base::StringPiece row_;
+  // If |position_| is >=0 and < |row_.size()|, then it points at the character
+  // to be read next from |row_|. If it is equal to |row_.size()|, then it means
+  // a fake trailing "," will be read next. If it is |row_.size() + 1|, then
+  // reading is done.
+  size_t position_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(FieldParser);
+};
+
+FieldParser::FieldParser(base::StringPiece row) : row_(row) {}
+
+FieldParser::~FieldParser() = default;
+
+bool FieldParser::NextField(base::StringPiece* field_contents) {
+  DCHECK(HasMoreFields());
+
+  if (state_ != State::kInit) {
+    state_ = State::kError;
+    return false;
+  }
+
+  const size_t start = position_;
+  do {
+    UpdateState();
+  } while (state_ != State::kInit && state_ != State::kError);
+
+  if (state_ != State::kError) {
+    DCHECK_GT(position_, start);  // There must have been at least the ','.
+    *field_contents =
+        base::StringPiece(row_.data() + start, position_ - start - 1);
+
+    if (field_contents->starts_with("\"")) {
+      DCHECK(field_contents->ends_with("\"")) << *field_contents;
+      DCHECK_GE(field_contents->size(), 2u);
+      field_contents->remove_prefix(1);
+      field_contents->remove_suffix(1);
+    }
+    return true;
+  }
+  return false;
+}
+
+char FieldParser::ConsumeChar() {
+  DCHECK_LE(position_, row_.size());
+  // The default character to return once all from |row_| are consumed and
+  // |position_| == |row_.size()|.
+  char ret = ',';
+  if (position_ < row_.size())
+    ret = row_[position_];
+  ++position_;
+  return ret;
+}
+
+void FieldParser::UpdateState() {
+  if (position_ > row_.size()) {
+    // If in state |kInit| then the program attempts to read one field too many.
+    DCHECK_NE(state_, State::kInit);
+    // Otherwise a quotation mark was not matched before the end of input.
+    state_ = State::kError;
+    return;
+  }
+
+  char read = ConsumeChar();
+  switch (state_) {
+    case State::kInit:
+      switch (read) {
+        case ',':
+          break;
+        case '"':
+          state_ = State::kQuoted;
+          break;
+        default:
+          state_ = State::kPlain;
+          break;
+      }
+      break;
+    case State::kPlain:
+      switch (read) {
+        case ',':
+          state_ = State::kInit;
+          break;
+        default:
+          break;
+      }
+      break;
+    case State::kQuoted:
+      switch (read) {
+        case '"':
+          state_ = State::kAfter;
+          break;
+        default:
+          break;
+      }
+      break;
+    case State::kAfter:
+      switch (read) {
+        case ',':
+          state_ = State::kInit;
+          break;
+        case '"':
+          state_ = State::kQuoted;
+          break;
+        default:
+          state_ = State::kError;
+          break;
+      }
+      break;
+    case State::kError:
+      NOTREACHED();
+      break;
+  }
+}
+
+// Created for a string with potentially multiple rows of
+// comma-separated-values, iteratively returns individual fields from row after
+// row.
 class CSVParser {
  public:
-  CSVParser(base::StringPiece csv)
-      : remaining_csv_piece_(csv.data(), csv.size()),
-        first_row_regex_(kFirstRowRE),
-        first_field_regex_(kFirstFieldRE) {}
+  explicit CSVParser(base::StringPiece csv);
+  ~CSVParser();
 
   // Reads and unescapes values from the next row, and writes them to |fields|.
-  // Consumes the EOL terminator. Returns false on syntax error.
+  // Consumes the end-of-line terminator. Returns false on syntax error. The
+  // input must not be empty (check with HasMoreRows() before calling).
   bool ParseNextCSVRow(std::vector<std::string>* fields);
 
   bool HasMoreRows() const { return !remaining_csv_piece_.empty(); }
 
  private:
-  re2::StringPiece remaining_csv_piece_;
-
-  const RE2 first_row_regex_;
-  const RE2 first_field_regex_;
+  base::StringPiece remaining_csv_piece_;
 
   DISALLOW_COPY_AND_ASSIGN(CSVParser);
 };
 
+CSVParser::CSVParser(base::StringPiece csv) : remaining_csv_piece_(csv) {}
+
+CSVParser::~CSVParser() = default;
+
 bool CSVParser::ParseNextCSVRow(std::vector<std::string>* fields) {
   fields->clear();
 
-  re2::StringPiece row;
-  if (!RE2::Consume(&remaining_csv_piece_, first_row_regex_, &row))
-    return false;
-
-  re2::StringPiece remaining_row_piece(row);
-  do {
-    re2::StringPiece field;
-    if (!RE2::Consume(&remaining_row_piece, first_field_regex_, &field))
+  DCHECK(HasMoreRows());
+  FieldParser parser(ConsumeLine(&remaining_csv_piece_));
+  base::StringPiece current_field;
+  while (parser.HasMoreFields()) {
+    if (!parser.NextField(&current_field))
       return false;
-    if (field.starts_with("\"")) {
-      CHECK(field.ends_with("\""));
-      CHECK_GE(field.size(), 2u);
-      field.remove_prefix(1);
-      field.remove_suffix(1);
-    }
-    std::string field_copy(field.as_string());
+    // TODO(crbug.com/918530): Unescape the field contents in-place, as part of
+    // NextField().
+    std::string field_copy(current_field);
     base::ReplaceSubstringsAfterOffset(&field_copy, 0, "\"\"", "\"");
-    fields->push_back(field_copy);
-  } while (!remaining_row_piece.empty());
-
-  if (row.ends_with(","))
-    fields->push_back(std::string());
-
+    fields->push_back(std::move(field_copy));
+  }
   return true;
 }
 
@@ -114,6 +302,12 @@
 
   // Read header row.
   CSVParser parser(normalized_csv);
+  if (!parser.HasMoreRows()) {
+    // The empty CSV is a special case. It can be seen as having one row, with a
+    // single field, which is an empty string.
+    column_names_.emplace_back();
+    return true;
+  }
   if (!parser.ParseNextCSVRow(&column_names_))
     return false;
 
diff --git a/components/password_manager/core/browser/import/csv_reader_unittest.cc b/components/password_manager/core/browser/import/csv_reader_unittest.cc
index c71067f2..5fc23a5 100644
--- a/components/password_manager/core/browser/import/csv_reader_unittest.cc
+++ b/components/password_manager/core/browser/import/csv_reader_unittest.cc
@@ -4,294 +4,217 @@
 
 #include "components/password_manager/core/browser/import/csv_reader.h"
 
+#include <utility>
+#include <vector>
+
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace password_manager {
 
-TEST(CSVReaderTest, EmptyCSV) {
-  CSVTable table;
-  EXPECT_TRUE(table.ReadCSV(std::string()));
-  EXPECT_THAT(table.column_names(), testing::ElementsAre(""));
-  EXPECT_EQ(0u, table.records().size());
+TEST(CSVReaderTest, Positive) {
+  struct TestCase {
+    const char* name;
+    const char* input;
+    std::vector<const char*> expected_column_names;
+    std::vector<std::vector<std::pair<const char*, const char*>>>
+        expected_row_maps;
+  };
+  const TestCase kCases[] = {
+      {
+          "EmptyCSV",
+          "",
+          {""},
+          {},
+      },
+      {
+          "CSVConsistingOfSingleNewLine",
+          "\n",
+          {""},
+          {},
+      },
+      {
+          "SingleColumn",
+          "foo\n"
+          "alpha\n"
+          "beta\n",
+          {"foo"},
+          {{{"foo", "alpha"}}, {{"foo", "beta"}}},
+      },
+      {
+          "HeaderOnly",
+          "foo,bar\n",
+          {"foo", "bar"},
+          {},
+      },
+      {
+          "NoNewline",
+          "foo,bar",
+          {"foo", "bar"},
+          {},
+      },
+      {
+          "HeaderAndSimpleRecords",
+          "foo,bar,baz\n"
+          "alpha,beta,gamma\n"
+          "delta,epsilon,zeta\n",
+          {"foo", "bar", "baz"},
+          {{{"bar", "beta"}, {"baz", "gamma"}, {"foo", "alpha"}},
+           {{"bar", "epsilon"}, {"baz", "zeta"}, {"foo", "delta"}}},
+      },
+      {
+          "EmptyStringColumnNamesAreSupported",
+          "foo,,bar\n"
+          "alpha,beta,gamma\n",
+          {"foo", "", "bar"},
+          {{{"", "beta"}, {"bar", "gamma"}, {"foo", "alpha"}}},
+      },
+      {
+          "ExtraSpacesArePreserved",
+          "left,right\n"
+          " alpha  beta ,  \n",
+          {"left", "right"},
+          {{{"left", " alpha  beta "}, {"right", "  "}}},
+      },
+      {
+          "CharactersOutsideASCIIPrintableArePreservedVerbatim",
+          "left,right\n"
+          "\x07\t\x0B\x1F,$\xc2\xa2\xe2\x98\x83\xf0\xa4\xad\xa2\n",
+          {"left", "right"},
+          {{// Characters below 0x20: bell, horizontal + vertical tab, unit
+            // separator.
+            {"left", "\x07\t\x0B\x1F"},
+            // Unicode code points having 1..4 byte UTF-8 representation: dollar
+            // sign (U+0024), cent sign (U+00A2), snowman (U+2603), Han
+            // character U+24B62.
+            {"right", "$\xc2\xa2\xe2\x98\x83\xf0\xa4\xad\xa2"}}},
+      },
+      {
+          "EnclosingDoubleQuotesAreTrimmed",
+          "\"left\",\"right\"\n"
+          "\"alpha\",\"beta\"\n",
+          {"left", "right"},
+          {{{"left", "alpha"}, {"right", "beta"}}},
+      },
+      {
+          "SeparatorsInsideDoubleQuotesAreTreatedVerbatim",
+          "left,right\n"
+          "\"A\rB\",\"B\nC\"\n"
+          "\"C\r\nD\",\"D\n\"\n"
+          "\",\",\",,\"\n",
+          {"left", "right"},
+          {{{"left", "A\rB"}, {"right", "B\nC"}},
+           {{"left", "C\nD"}, {"right", "D\n"}},
+           {{"left", ","}, {"right", ",,"}}},
+      },
+      {
+          "EscapedDoubleQuotes",
+          "left,right\n"
+          R"("","""""")"
+          "\n"
+          R"("""","A""B""""C")"
+          "\n",
+          {"left", "right"},
+          {{{"left", ""}, {"right", "\"\""}},
+           {{"left", "\""}, {"right", "A\"B\"\"C"}}},
+      },
+      {
+          "InconsistentFieldsCountIsTreatedGracefully",
+          "left,right\n"
+          "A\n"
+          "B,C,D\n",
+          {"left", "right"},
+          {{{"left", "A"}}, {{"left", "B"}, {"right", "C"}}},
+      },
+      {
+          "SupportMissingNewLineAtEOF",
+          "left,right\n"
+          "alpha,beta",
+          {"left", "right"},
+          {{{"left", "alpha"}, {"right", "beta"}}},
+      },
+      {
+          "EmptyFields",
+          "left,middle,right\n"
+          "alpha,beta,\n"
+          ",,gamma\n",
+          {"left", "middle", "right"},
+          {{{"left", "alpha"}, {"middle", "beta"}, {"right", ""}},
+           {{"left", ""}, {"middle", ""}, {"right", "gamma"}}},
+      },
+      {
+          "CRLFTreatedAsAndConvertedToLF",
+          "left,right\r\n"
+          "\"\r\",\"\r\n\"\r\n",
+          {"left", "right"},
+          {{{"left", "\r"}, {"right", "\n"}}},
+      },
+      {
+          "LastValueForRepeatedColumnNamesIsPreserved",
+          "foo,bar,bar\n"
+          "alpha,beta,gamma\n",
+          {"foo", "bar", "bar"},
+          {{{"bar", "gamma"}, {"foo", "alpha"}}},
+      },
+      {
+          "EmptyLastFieldAndNoNewline",
+          "alpha,",
+          {"alpha", ""},
+          {},
+      },
+  };
+
+  for (const TestCase& test_case : kCases) {
+    SCOPED_TRACE(test_case.name);
+    CSVTable table;
+    ASSERT_TRUE(table.ReadCSV(test_case.input));
+
+    EXPECT_THAT(table.column_names(),
+                testing::ElementsAreArray(test_case.expected_column_names));
+    ASSERT_EQ(test_case.expected_row_maps.size(), table.records().size());
+    for (size_t i = 0; i < test_case.expected_row_maps.size(); ++i) {
+      EXPECT_THAT(table.records()[i],
+                  testing::ElementsAreArray(test_case.expected_row_maps[i]));
+    }
+  }
 }
 
-TEST(CSVReaderTest, CSVConsistingOfSingleNewLine) {
-  CSVTable table;
-  EXPECT_TRUE(table.ReadCSV(std::string("\n")));
-  EXPECT_THAT(table.column_names(), testing::ElementsAre(""));
-  EXPECT_EQ(0u, table.records().size());
-}
+TEST(CSVReaderTest, Negative) {
+  struct TestCase {
+    const char* name;
+    const char* input;
+  };
+  const TestCase kCases[] = {
+      {
+          "FailureWhenEOFInsideQuotes",
+          "left,right\n"
+          "\"alpha\",\"unmatched\n",
+      },
+      {
+          "FailureWhenSemiQuotedContentInHeader",
+          "\"a\"b\"c\",right\n"
+          "alpha,beta\n",
+      },
+      {
+          "FailureWhenSemiQuotedContentOnSubsequentLine",
+          "alpha,beta\n"
+          "left,\"a\"b\"c\"\n",
+      },
+      {
+          "FailureWhenJustOneQuote",
+          "\"",
+      },
+      {
+          "FailureWhenJustOneQuoteAndComma",
+          "\",",
+      },
+  };
 
-TEST(CSVReaderTest, SingleColumn) {
-  const char kTestCSVInput[] =
-      "foo\n"
-      "alpha\n"
-      "beta\n";
-
-  CSVTable table;
-  EXPECT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("foo"));
-  ASSERT_EQ(2u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("foo", "alpha")));
-  EXPECT_THAT(table.records()[1],
-              testing::ElementsAre(std::make_pair("foo", "beta")));
-}
-
-TEST(CSVReaderTest, HeaderOnly) {
-  const char kTestCSVInput[] =
-      "foo,bar\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("foo", "bar"));
-  EXPECT_EQ(0u, table.records().size());
-}
-
-TEST(CSVReaderTest, HeaderAndSimpleRecords) {
-  const char kTestCSVInput[] =
-      "foo,bar,baz\n"
-      "alpha,beta,gamma\n"
-      "delta,epsilon,zeta\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("foo", "bar", "baz"));
-  ASSERT_EQ(2u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("bar", "beta"),
-                                   std::make_pair("baz", "gamma"),
-                                   std::make_pair("foo", "alpha")));
-  EXPECT_THAT(table.records()[1],
-              testing::ElementsAre(std::make_pair("bar", "epsilon"),
-                                   std::make_pair("baz", "zeta"),
-                                   std::make_pair("foo", "delta")));
-}
-
-TEST(CSVReaderTest, EmptyStringColumnNamesAreSupported) {
-  const char kTestCSVInput[] =
-      "foo,,bar\n"
-      "alpha,beta,gamma\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("foo", "", "bar"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("", "beta"),
-                                   std::make_pair("bar", "gamma"),
-                                   std::make_pair("foo", "alpha")));
-}
-
-TEST(CSVReaderTest, ExtraSpacesArePreserved) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      " alpha  beta ,  \n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", " alpha  beta "),
-                                   std::make_pair("right", "  ")));
-}
-
-TEST(CSVReaderTest, CharactersOutsideASCIIPrintableArePreservedVerbatim) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      "\x07\t\x0B\x1F,$\xc2\xa2\xe2\x98\x83\xf0\xa4\xad\xa2\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(
-      table.records()[0],
-      testing::ElementsAre(
-          // Characters below 0x20: bell, horizontal + vertical tab, unit
-          // separator.
-          std::make_pair("left", "\x07\t\x0B\x1F"),
-          // Unicode code points having 1..4 byte UTF-8 representation: dollar
-          // sign (U+0024), cent sign (U+00A2), snowman (U+2603), Han character
-          // U+24B62.
-          std::make_pair("right", "$\xc2\xa2\xe2\x98\x83\xf0\xa4\xad\xa2")));
-}
-
-TEST(CSVReaderTest, EnclosingDoubleQuotesAreTrimmed) {
-  const char kTestCSVInput[] =
-      "\"left\",\"right\"\n"
-      "\"alpha\",\"beta\"\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", "alpha"),
-                                   std::make_pair("right", "beta")));
-}
-
-TEST(CSVReaderTest, SeparatorsInsideDoubleQuotesAreTreatedVerbatim) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      "\"A\rB\",\"B\nC\"\n"
-      "\"C\r\nD\",\"D\n\"\n"
-      "\",\",\",,\"\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(3u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", "A\rB"),
-                                   std::make_pair("right", "B\nC")));
-  EXPECT_THAT(table.records()[1],
-              testing::ElementsAre(std::make_pair("left", "C\nD"),
-                                   std::make_pair("right", "D\n")));
-  EXPECT_THAT(table.records()[2],
-              testing::ElementsAre(std::make_pair("left", ","),
-                                   std::make_pair("right", ",,")));
-}
-
-TEST(CSVReaderTest, EscapedDoubleQuotes) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      "\"\",\"\"\"\"\"\"\n"
-      "\"\"\"\",\"A\"\"B\"\"\"\"C\"\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(2u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", ""),
-                                   std::make_pair("right", "\"\"")));
-  EXPECT_THAT(table.records()[1],
-              testing::ElementsAre(std::make_pair("left", "\""),
-                                   std::make_pair("right", "A\"B\"\"C")));
-}
-
-TEST(CSVReaderTest, InconsistentFieldsCountIsTreatedGracefully) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      "A\n"
-      "B,C,D\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(2u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", "A")));
-  EXPECT_THAT(table.records()[1],
-              testing::ElementsAre(std::make_pair("left", "B"),
-                                   std::make_pair("right", "C")));
-}
-
-TEST(CSVReaderTest, SupportMissingNewLineAtEOF) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      "alpha,beta";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", "alpha"),
-                                   std::make_pair("right", "beta")));
-}
-
-TEST(CSVReaderTest, EmptyFields) {
-  const char kTestCSVInput[] =
-      "left,middle,right\n"
-      "alpha,beta,\n"
-      ",,gamma\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(),
-              testing::ElementsAre("left", "middle", "right"));
-  ASSERT_EQ(2u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", "alpha"),
-                                   std::make_pair("middle", "beta"),
-                                   std::make_pair("right", "")));
-  EXPECT_THAT(table.records()[1],
-              testing::ElementsAre(std::make_pair("left", ""),
-                                   std::make_pair("middle", ""),
-                                   std::make_pair("right", "gamma")));
-}
-
-TEST(CSVReaderTest, CRLFTreatedAsAndConvertedToLF) {
-  const char kTestCSVInput[] =
-      "left,right\r\n"
-      "\"\r\",\"\r\n\"\r\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("left", "right"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("left", "\r"),
-                                   std::make_pair("right", "\n")));
-}
-
-TEST(CSVReaderTest, LastValueForRepeatedColumnNamesIsPreserved) {
-  const char kTestCSVInput[] =
-      "foo,bar,bar\n"
-      "alpha,beta,gamma\n";
-
-  CSVTable table;
-  ASSERT_TRUE(table.ReadCSV(kTestCSVInput));
-
-  EXPECT_THAT(table.column_names(), testing::ElementsAre("foo", "bar", "bar"));
-  ASSERT_EQ(1u, table.records().size());
-  EXPECT_THAT(table.records()[0],
-              testing::ElementsAre(std::make_pair("bar", "gamma"),
-                                   std::make_pair("foo", "alpha")));
-}
-
-TEST(CSVReaderTest, FailureWhenEOFInsideQuotes) {
-  const char kTestCSVInput[] =
-      "left,right\n"
-      "\"alpha\",\"unmatched\n";
-
-  CSVTable table;
-  ASSERT_FALSE(table.ReadCSV(kTestCSVInput));
-}
-
-TEST(CSVReaderTest, FailureWhenSemiQuotedContentInHeader) {
-  const char kTestCSVInput[] =
-      "\"a\"b\"c\",right\n"
-      "alpha,beta\n";
-
-  CSVTable table;
-  ASSERT_FALSE(table.ReadCSV(kTestCSVInput));
-}
-
-TEST(CSVReaderTest, FailureWhenSemiQuotedContentOnSubsequentLine) {
-  const char kTestCSVInput[] =
-      "alpha,beta\n"
-      "left,\"a\"b\"c\"\n";
-
-  CSVTable table;
-  ASSERT_FALSE(table.ReadCSV(kTestCSVInput));
+  for (const TestCase& test_case : kCases) {
+    SCOPED_TRACE(test_case.name);
+    CSVTable table;
+    EXPECT_FALSE(table.ReadCSV(test_case.input));
+  }
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.cc b/components/password_manager/core/browser/password_form_metrics_recorder.cc
index c40a5301..360fb3a 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.cc
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.cc
@@ -415,14 +415,14 @@
 
   // Consider cases when the user typed known or unknown credentials.
   if (username_password_state.saved_password_typed) {
-    filling_assistance_ = FillingAssistance::kPasswordTyped;
+    filling_assistance_ = FillingAssistance::kKnownPasswordTyped;
     return;
   }
 
   if (!username_password_state.IsPasswordFilled()) {
     filling_assistance_ =
         username_password_state.unknown_password_typed
-            ? FillingAssistance::kNewCredentialsTyped
+            ? FillingAssistance::kNewPasswordTypedWhileCredentialsExisted
             : FillingAssistance::kNoUserInputNoFillingInPasswordFields;
     return;
   }
@@ -445,8 +445,8 @@
     return;
   }
 
-  filling_assistance_ =
-      FillingAssistance::kNoUserInputNoFillingInPasswordFields;
+  // If execution gets here, we have a bug in our state machine.
+  NOTREACHED();
 }
 
 int PasswordFormMetricsRecorder::GetActionsTaken() const {
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.h b/components/password_manager/core/browser/password_form_metrics_recorder.h
index 09ee775a..1b14be4 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -259,10 +259,9 @@
     // typed.
     kUsernameTypedPasswordFilled = 2,
     // Known password was typed.
-    kPasswordTyped = 3,
-    // Unknown credentials were typed (neither username nor password were known)
-    // while some credentials were stored.
-    kNewCredentialsTyped = 4,
+    kKnownPasswordTyped = 3,
+    // Unknown password was typed while some credentials were stored.
+    kNewPasswordTypedWhileCredentialsExisted = 4,
     // No saved credentials.
     kNoSavedCredentials = 5,
     // Neither user input nor filling.
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc b/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
index 60631817..8bb5d4c3 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
+++ b/components/password_manager/core/browser/password_form_metrics_recorder_unittest.cc
@@ -894,8 +894,8 @@
        .saved_usernames = {"user1", "user2"},
        .saved_passwords = {"password1", "password2"},
 
-       .expectation =
-           PasswordFormMetricsRecorder::FillingAssistance::kPasswordTyped});
+       .expectation = PasswordFormMetricsRecorder::FillingAssistance::
+           kKnownPasswordTyped});
 }
 
 TEST(PasswordFormMetricsRecorder, FillingAssistanceUserTypedUsername) {
@@ -927,7 +927,7 @@
        .saved_passwords = {"password1", "password2"},
 
        .expectation = PasswordFormMetricsRecorder::FillingAssistance::
-           kNewCredentialsTyped});
+           kNewPasswordTypedWhileCredentialsExisted});
 }
 
 TEST(PasswordFormMetricsRecorder, FillingAssistanceChangePasswordForm) {
diff --git a/components/payments/content/android/payment_manifest_downloader_android.cc b/components/payments/content/android/payment_manifest_downloader_android.cc
index 6c101cfb..519a45ab 100644
--- a/components/payments/content/android/payment_manifest_downloader_android.cc
+++ b/components/payments/content/android/payment_manifest_downloader_android.cc
@@ -27,7 +27,8 @@
 
   ~DownloadCallback() {}
 
-  void OnPaymentMethodManifestDownload(const std::string& content) {
+  void OnPaymentMethodManifestDownload(const GURL& url_after_redirects,
+                                       const std::string& content) {
     JNIEnv* env = base::android::AttachCurrentThread();
 
     if (content.empty()) {
@@ -39,7 +40,8 @@
     }
   }
 
-  void OnWebAppManifestDownload(const std::string& content) {
+  void OnWebAppManifestDownload(const GURL& url_after_redirects,
+                                const std::string& content) {
     JNIEnv* env = base::android::AttachCurrentThread();
 
     if (content.empty()) {
diff --git a/components/payments/content/installable_payment_app_crawler.cc b/components/payments/content/installable_payment_app_crawler.cc
index 0785101..3998fd42 100644
--- a/components/payments/content/installable_payment_app_crawler.cc
+++ b/components/payments/content/installable_payment_app_crawler.cc
@@ -83,9 +83,30 @@
   }
 }
 
+void InstallablePaymentAppCrawler::IgnorePortInOriginComparisonForTesting() {
+  ignore_port_in_origin_comparison_for_testing_ = true;
+}
+
+bool InstallablePaymentAppCrawler::IsSameOriginWith(const GURL& a,
+                                                    const GURL& b) {
+  if (ignore_port_in_origin_comparison_for_testing_) {
+    GURL::Replacements replacements;
+    replacements.ClearPort();
+    return url::IsSameOriginWith(a.ReplaceComponents(replacements),
+                                 b.ReplaceComponents(replacements));
+  }
+  return url::IsSameOriginWith(a, b);
+}
+
 void InstallablePaymentAppCrawler::OnPaymentMethodManifestDownloaded(
     const GURL& method_manifest_url,
+    const GURL& method_manifest_url_after_redirects,
     const std::string& content) {
+  // Enforced in PaymentManifestDownloader.
+  DCHECK(net::registry_controlled_domains::SameDomainOrHost(
+      method_manifest_url, method_manifest_url_after_redirects,
+      net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
+
   number_of_payment_method_manifest_to_download_--;
   if (content.empty()) {
     FinishCrawlingPaymentAppsIfReady();
@@ -96,11 +117,13 @@
   parser_->ParsePaymentMethodManifest(
       content, base::BindOnce(
                    &InstallablePaymentAppCrawler::OnPaymentMethodManifestParsed,
-                   weak_ptr_factory_.GetWeakPtr(), method_manifest_url));
+                   weak_ptr_factory_.GetWeakPtr(), method_manifest_url,
+                   method_manifest_url_after_redirects));
 }
 
 void InstallablePaymentAppCrawler::OnPaymentMethodManifestParsed(
     const GURL& method_manifest_url,
+    const GURL& method_manifest_url_after_redirects,
     const std::vector<GURL>& default_applications,
     const std::vector<url::Origin>& supported_origins,
     bool all_origins_supported) {
@@ -113,37 +136,41 @@
           web_contents()->GetBrowserContext());
   DCHECK(permission_controller);
 
-  for (const auto& url : default_applications) {
-    if (downloaded_web_app_manifests_.find(url) !=
+  for (const auto& web_app_manifest_url : default_applications) {
+    if (downloaded_web_app_manifests_.find(web_app_manifest_url) !=
         downloaded_web_app_manifests_.end()) {
       // Do not download the same web app manifest again since a web app could
       // be the default application of multiple payment methods.
       continue;
     }
 
-    if (!net::registry_controlled_domains::SameDomainOrHost(
-            method_manifest_url, url,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      log_.Warn("Installable payment handler from \"" + url.spec() +
-                "\" is not allowed for the method \"" +
-                method_manifest_url.spec() + "\" because of different domain.");
+    if (!IsSameOriginWith(method_manifest_url_after_redirects,
+                          web_app_manifest_url)) {
+      log_.Error("Installable payment handler from \"" +
+                 web_app_manifest_url.spec() +
+                 "\" is not allowed for the method \"" +
+                 method_manifest_url_after_redirects.spec() +
+                 "\" because of different origin.");
       continue;
     }
 
     if (permission_controller->GetPermissionStatus(
-            content::PermissionType::PAYMENT_HANDLER, url.GetOrigin(),
-            url.GetOrigin()) != blink::mojom::PermissionStatus::GRANTED) {
+            content::PermissionType::PAYMENT_HANDLER,
+            web_app_manifest_url.GetOrigin(),
+            web_app_manifest_url.GetOrigin()) !=
+        blink::mojom::PermissionStatus::GRANTED) {
       // Do not download the web app manifest if it is blocked.
       continue;
     }
 
     number_of_web_app_manifest_to_download_++;
-    downloaded_web_app_manifests_.insert(url);
+    downloaded_web_app_manifests_.insert(web_app_manifest_url);
     downloader_->DownloadWebAppManifest(
-        url,
+        web_app_manifest_url,
         base::BindOnce(
             &InstallablePaymentAppCrawler::OnPaymentWebAppManifestDownloaded,
-            weak_ptr_factory_.GetWeakPtr(), method_manifest_url, url));
+            weak_ptr_factory_.GetWeakPtr(), method_manifest_url,
+            web_app_manifest_url));
   }
 
   FinishCrawlingPaymentAppsIfReady();
@@ -152,7 +179,19 @@
 void InstallablePaymentAppCrawler::OnPaymentWebAppManifestDownloaded(
     const GURL& method_manifest_url,
     const GURL& web_app_manifest_url,
+    const GURL& web_app_manifest_url_after_redirects,
     const std::string& content) {
+#if DCHECK_IS_ON()
+  GURL::Replacements replacements;
+  if (ignore_port_in_origin_comparison_for_testing_)
+    replacements.ClearPort();
+
+  // Enforced in PaymentManifestDownloader.
+  DCHECK_EQ(
+      web_app_manifest_url.ReplaceComponents(replacements),
+      web_app_manifest_url_after_redirects.ReplaceComponents(replacements));
+#endif  // DCHECK_IS_ON()
+
   number_of_web_app_manifest_to_download_--;
   if (content.empty()) {
     FinishCrawlingPaymentAppsIfReady();
@@ -212,20 +251,26 @@
                  app_info->sw_js_url + "\".");
       return false;
     }
-    if (!net::registry_controlled_domains::SameDomainOrHost(
-            method_manifest_url, absolute_url,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      log_.Error(log_prefix +
-                 "Installable payment handler's service worker JavaScript file "
-                 "URL \"" +
-                 absolute_url.spec() + "\" is not allowed for the method \"" +
-                 method_manifest_url.spec() +
-                 "\" because of different domain.");
-      return false;
-    }
     app_info->sw_js_url = absolute_url.spec();
   }
 
+  if (!IsSameOriginWith(web_app_manifest_url, GURL(app_info->sw_js_url))) {
+    log_.Error(log_prefix +
+               "Installable payment handler's service worker JavaScript file "
+               "URL \"" +
+               app_info->sw_js_url +
+               "\" is not allowed for the web app manifest file \"" +
+               web_app_manifest_url.spec() + "\" because of different origin.");
+    return false;
+  }
+
+  if (!app_info->sw_scope.empty() && !base::IsStringUTF8(app_info->sw_scope)) {
+    log_.Error(log_prefix +
+               "The installable payment handler's service worker scope is not "
+               "a UTF8 string.");
+    return false;
+  }
+
   if (!GURL(app_info->sw_scope).is_valid()) {
     GURL absolute_scope =
         web_app_manifest_url.GetWithoutFilename().Resolve(app_info->sw_scope);
@@ -236,18 +281,18 @@
                  app_info->sw_scope + "\".");
       return false;
     }
-    if (!net::registry_controlled_domains::SameDomainOrHost(
-            method_manifest_url, absolute_scope,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
-      log_.Error(
-          log_prefix + "Installable payment handler's registration scope \"" +
-          absolute_scope.spec() + "\" is not allowed for the method \"" +
-          method_manifest_url.spec() + "\" because of different domain.");
-      return false;
-    }
     app_info->sw_scope = absolute_scope.spec();
   }
 
+  if (!IsSameOriginWith(web_app_manifest_url, GURL(app_info->sw_scope))) {
+    log_.Error(log_prefix +
+               "Installable payment handler's registration scope \"" +
+               app_info->sw_scope +
+               "\" is not allowed for the web app manifest file \"" +
+               web_app_manifest_url.spec() + "\" because of different origin.");
+    return false;
+  }
+
   std::string error_message;
   if (!content::PaymentAppProvider::GetInstance()->IsValidInstallablePaymentApp(
           web_app_manifest_url, GURL(app_info->sw_js_url),
@@ -261,7 +306,7 @@
   if (installable_apps_.find(method_manifest_url) != installable_apps_.end()) {
     log_.Error(log_prefix +
                "Multiple installable payment handlers from for a single "
-               "payment method: not yet supported.");
+               "payment method are not yet supported.");
     return false;
   }
 
@@ -377,8 +422,7 @@
   } else {
     auto it = installable_apps_.find(method_manifest_url);
     DCHECK(it != installable_apps_.end());
-    DCHECK(url::IsSameOriginWith(GURL(it->second->sw_scope),
-                                 web_app_manifest_url));
+    DCHECK(IsSameOriginWith(GURL(it->second->sw_scope), web_app_manifest_url));
 
     it->second->icon = std::make_unique<SkBitmap>(icon);
   }
diff --git a/components/payments/content/installable_payment_app_crawler.h b/components/payments/content/installable_payment_app_crawler.h
index a7b50e8..601667c 100644
--- a/components/payments/content/installable_payment_app_crawler.h
+++ b/components/payments/content/installable_payment_app_crawler.h
@@ -57,17 +57,26 @@
       FinishedCrawlingCallback callback,
       base::OnceClosure finished_using_resources);
 
+  void IgnorePortInOriginComparisonForTesting();
+
  private:
-  void OnPaymentMethodManifestDownloaded(const GURL& method_manifest_url,
-                                         const std::string& content);
+  bool IsSameOriginWith(const GURL& a, const GURL& b);
+
+  void OnPaymentMethodManifestDownloaded(
+      const GURL& method_manifest_url,
+      const GURL& method_manifest_url_after_redirects,
+      const std::string& content);
   void OnPaymentMethodManifestParsed(
       const GURL& method_manifest_url,
+      const GURL& method_manifest_url_after_redirects,
       const std::vector<GURL>& default_applications,
       const std::vector<url::Origin>& supported_origins,
       bool all_origins_supported);
-  void OnPaymentWebAppManifestDownloaded(const GURL& method_manifest_url,
-                                         const GURL& web_app_manifest_url,
-                                         const std::string& content);
+  void OnPaymentWebAppManifestDownloaded(
+      const GURL& method_manifest_url,
+      const GURL& web_app_manifest_url,
+      const GURL& web_app_manifest_url_after_redirects,
+      const std::string& content);
   void OnPaymentWebAppInstallationInfo(
       const GURL& method_manifest_url,
       const GURL& web_app_manifest_url,
@@ -100,6 +109,8 @@
   std::set<GURL> downloaded_web_app_manifests_;
   std::map<GURL, std::unique_ptr<WebAppInstallationInfo>> installable_apps_;
 
+  bool ignore_port_in_origin_comparison_for_testing_ = false;
+
   base::WeakPtrFactory<InstallablePaymentAppCrawler> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(InstallablePaymentAppCrawler);
diff --git a/components/payments/content/manifest_verifier.cc b/components/payments/content/manifest_verifier.cc
index ca1d6e27..29fa3aa 100644
--- a/components/payments/content/manifest_verifier.cc
+++ b/components/payments/content/manifest_verifier.cc
@@ -213,6 +213,7 @@
 
 void ManifestVerifier::OnPaymentMethodManifestDownloaded(
     const GURL& method_manifest_url,
+    const GURL& unused_method_manifest_url_after_redirects,
     const std::string& content) {
   DCHECK_LT(0U, number_of_manifests_to_download_);
 
diff --git a/components/payments/content/manifest_verifier.h b/components/payments/content/manifest_verifier.h
index 0e189540..85317fe 100644
--- a/components/payments/content/manifest_verifier.h
+++ b/components/payments/content/manifest_verifier.h
@@ -78,9 +78,12 @@
       WebDataServiceBase::Handle h,
       std::unique_ptr<WDTypedResult> result) override;
 
-  // Called when a manifest is downloaded.
-  void OnPaymentMethodManifestDownloaded(const GURL& method_manifest_url,
-                                         const std::string& content);
+  // Called when a manifest is downloaded. The "method manifest URL after
+  // redirects" is intentionally not used.
+  void OnPaymentMethodManifestDownloaded(
+      const GURL& method_manifest_url,
+      const GURL& unused_method_manifest_url_after_redirects,
+      const std::string& content);
 
   // Called when a manifest is parsed.
   void OnPaymentMethodManifestParsed(
diff --git a/components/payments/content/service_worker_payment_app_factory.cc b/components/payments/content/service_worker_payment_app_factory.cc
index f516cb9..a2b396b 100644
--- a/components/payments/content/service_worker_payment_app_factory.cc
+++ b/components/payments/content/service_worker_payment_app_factory.cc
@@ -81,7 +81,7 @@
 
 void RemovePortNumbersFromScopesForTest(
     content::PaymentAppProvider::PaymentApps* apps) {
-  url::Replacements<char> replacements;
+  GURL::Replacements replacements;
   replacements.ClearPort();
   for (auto& app : *apps) {
     app.second->scope = app.second->scope.ReplaceComponents(replacements);
@@ -120,6 +120,8 @@
       // Construct crawler in constructor to allow it observe the web_contents.
       crawler_ = std::make_unique<InstallablePaymentAppCrawler>(
           web_contents, downloader_.get(), parser_.get(), cache_.get());
+      if (ignore_port_in_origin_comparison_for_testing_)
+        crawler_->IgnorePortInOriginComparisonForTesting();
     }
 
     // Method data cannot be copied and is passed in as a const-ref, which
@@ -138,13 +140,13 @@
             base::Unretained(this)));
   }
 
-  void IgnorePortInAppScopeForTesting() {
-    ignore_port_in_app_scope_for_testing_ = true;
+  void IgnorePortInOriginComparisonForTesting() {
+    ignore_port_in_origin_comparison_for_testing_ = true;
   }
 
  private:
   void OnGotAllPaymentApps(content::PaymentAppProvider::PaymentApps apps) {
-    if (ignore_port_in_app_scope_for_testing_)
+    if (ignore_port_in_origin_comparison_for_testing_)
       RemovePortNumbersFromScopesForTest(&apps);
 
     ServiceWorkerPaymentAppFactory::RemoveAppsWithoutMatchingMethodData(
@@ -238,7 +240,7 @@
   std::unique_ptr<InstallablePaymentAppCrawler> crawler_;
   bool is_payment_app_crawler_finished_using_resources_ = true;
 
-  bool ignore_port_in_app_scope_for_testing_ = false;
+  bool ignore_port_in_origin_comparison_for_testing_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(SelfDeletingServiceWorkerPaymentAppFactory);
 };
@@ -263,7 +265,7 @@
   std::unique_ptr<PaymentManifestDownloader> downloader;
   if (test_downloader_ != nullptr) {
     downloader = std::move(test_downloader_);
-    self_delete_factory->IgnorePortInAppScopeForTesting();
+    self_delete_factory->IgnorePortInOriginComparisonForTesting();
   } else {
     downloader = std::make_unique<payments::PaymentManifestDownloader>(
         std::make_unique<DeveloperConsoleLogger>(web_contents),
@@ -298,7 +300,7 @@
 }
 
 void ServiceWorkerPaymentAppFactory::
-    SetDownloaderAndIgnorePortInAppScopeForTesting(
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting(
         std::unique_ptr<PaymentManifestDownloader> downloader) {
   test_downloader_ = std::move(downloader);
 }
diff --git a/components/payments/content/service_worker_payment_app_factory.h b/components/payments/content/service_worker_payment_app_factory.h
index aa28553..496ca9e9 100644
--- a/components/payments/content/service_worker_payment_app_factory.h
+++ b/components/payments/content/service_worker_payment_app_factory.h
@@ -84,7 +84,7 @@
   // Should be used only in tests.
   // Should be called before every call to GetAllPaymentApps() (because the test
   // downloader is moved into the SelfDeletingServiceWorkerPaymentAppFactory).
-  void SetDownloaderAndIgnorePortInAppScopeForTesting(
+  void SetDownloaderAndIgnorePortInOriginComparisonForTesting(
       std::unique_ptr<PaymentManifestDownloader> downloader);
 
   std::unique_ptr<PaymentManifestDownloader> test_downloader_;
diff --git a/components/payments/core/payment_manifest_downloader.cc b/components/payments/core/payment_manifest_downloader.cc
index f507a0b..999976a 100644
--- a/components/payments/core/payment_manifest_downloader.cc
+++ b/components/payments/core/payment_manifest_downloader.cc
@@ -29,14 +29,13 @@
 namespace payments {
 namespace {
 
-GURL ParseResponseHeader(const GURL& original_url,
-                         const GURL& final_url,
+GURL ParseResponseHeader(const GURL& url,
                          scoped_refptr<net::HttpResponseHeaders> headers,
                          const ErrorLogger& log) {
   if (!headers) {
     log.Error(base::StringPrintf(
         "No HTTP headers found on \"%s\" for payment method manifest.",
-        final_url.spec().c_str()));
+        url.spec().c_str()));
     return GURL();
   }
 
@@ -44,7 +43,7 @@
   if (response_code != net::HTTP_OK && response_code != net::HTTP_NO_CONTENT) {
     log.Error(base::StringPrintf(
         "Unable to make a HEAD request to \"%s\" for payment method manifest.",
-        final_url.spec().c_str()));
+        url.spec().c_str()));
     return GURL();
   }
 
@@ -53,7 +52,7 @@
   if (link_header.empty()) {
     log.Error(base::StringPrintf(
         "No HTTP Link headers found on \"%s\" for payment method manifest.",
-        final_url.spec().c_str()));
+        url.spec().c_str()));
     return GURL();
   }
 
@@ -73,13 +72,13 @@
         base::SplitString(rel->second.value_or(""), HTTP_LWS,
                           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
     if (base::ContainsValue(rel_parts, "payment-method-manifest"))
-      return original_url.Resolve(payment_method_manifest_url);
+      return url.Resolve(payment_method_manifest_url);
   }
 
-  log.Error(base::StringPrintf(
-      "No rel=\"payment-method-manifest\" HTTP Link headers found on \"%s\" "
-      "for payment method manifest.",
-      final_url.spec().c_str()));
+  log.Error(
+      base::StringPrintf("No rel=\"payment-method-manifest\" HTTP Link headers "
+                         "found on \"%s\" for payment method manifest.",
+                         url.spec().c_str()));
   return GURL();
 }
 
@@ -99,15 +98,15 @@
 GURL ParseRedirectUrl(const net::RedirectInfo& redirect_info,
                       const ErrorLogger& log) {
   // Do not follow net::HTTP_MULTIPLE_CHOICES, net::HTTP_NOT_MODIFIED and
-  // net::HTTP_USE_PROXY redirects.
-  if (redirect_info.status_code != net::HTTP_MOVED_PERMANENTLY &&
-      redirect_info.status_code != net::HTTP_FOUND &&
-      redirect_info.status_code != net::HTTP_SEE_OTHER &&
-      redirect_info.status_code != net::HTTP_TEMPORARY_REDIRECT &&
-      redirect_info.status_code != net::HTTP_PERMANENT_REDIRECT) {
+  // net::HTTP_USE_PROXY redirects. (306 is no longer used.)
+  if (redirect_info.status_code != net::HTTP_MOVED_PERMANENTLY &&   // 301
+      redirect_info.status_code != net::HTTP_FOUND &&               // 302
+      redirect_info.status_code != net::HTTP_SEE_OTHER &&           // 303
+      redirect_info.status_code != net::HTTP_TEMPORARY_REDIRECT &&  // 307
+      redirect_info.status_code != net::HTTP_PERMANENT_REDIRECT) {  // 308
     log.Error(
-        "Cannot follow HTTP_MULTIPLE_CHOICES, HTTP_NOT_MODIFIED, and "
-        "HTTP_USE_PROXY redirects for payment manifests.");
+        "Cannot follow HTTP_MULTIPLE_CHOICES (300), HTTP_NOT_MODIFIED (304), "
+        "and HTTP_USE_PROXY (305) redirects for payment manifests.");
     return GURL();
   }
 
@@ -200,7 +199,7 @@
         "number of redirects.");
   }
 
-  std::move(download->callback).Run(std::string());
+  std::move(download->callback).Run(download->original_url, std::string());
 }
 
 void PaymentManifestDownloader::OnURLLoaderComplete(
@@ -231,21 +230,35 @@
   std::unique_ptr<Download> download = std::move(download_it->second);
   downloads_.erase(download_it);
 
-  if (download->method == "HEAD") {
-    GURL url =
-        ParseResponseHeader(download->original_url, final_url, headers, *log_);
-    if (IsValidManifestUrl(url, *log_)) {
-      InitiateDownload(url, "GET",
-                       /*allowed_number_of_redirects=*/0,
-                       std::move(download->callback));
-    } else {
-      std::move(download->callback).Run(std::string());
-    }
+  if (download->method == "GET") {
+    std::move(download->callback)
+        .Run(final_url,
+             ParseResponseContent(final_url, response_body, headers, *log_));
     return;
   }
 
-  std::move(download->callback)
-      .Run(ParseResponseContent(final_url, response_body, headers, *log_));
+  DCHECK_EQ("HEAD", download->method);
+  GURL payment_method_manifest_url =
+      ParseResponseHeader(final_url, headers, *log_);
+
+  if (!payment_method_manifest_url.is_valid() ||
+      !IsValidManifestUrl(payment_method_manifest_url, *log_)) {
+    std::move(download->callback).Run(final_url, std::string());
+    return;
+  }
+
+  if (!url::IsSameOriginWith(final_url, payment_method_manifest_url)) {
+    log_->Error(base::StringPrintf(
+        "Payment method manifest \"%s\" is not allowed for the method \"%s\" "
+        "because of different origin.",
+        payment_method_manifest_url.spec().c_str(), final_url.spec().c_str()));
+    std::move(download->callback).Run(final_url, std::string());
+    return;
+  }
+
+  InitiateDownload(payment_method_manifest_url, "GET",
+                   /*allowed_number_of_redirects=*/0,
+                   std::move(download->callback));
 }
 
 network::SimpleURLLoader* PaymentManifestDownloader::GetLoaderForTesting() {
diff --git a/components/payments/core/payment_manifest_downloader.h b/components/payments/core/payment_manifest_downloader.h
index 6af6345..a5dc93e6 100644
--- a/components/payments/core/payment_manifest_downloader.h
+++ b/components/payments/core/payment_manifest_downloader.h
@@ -30,26 +30,34 @@
 
 class ErrorLogger;
 
-// Called on completed download of a manifest. Download failure results in empty
-// contents. Failure to download the manifest can happen because of the
-// following reasons:
+// Called on completed download of a manifest |contents| from |url|, which is
+// the final URL after following the redirects, if any.
+//
+// Download failure results in empty contents. Failure to download the manifest
+// can happen because of the following reasons:
 //  - HTTP response code is not 200. (204 is also allowed for HEAD request.)
 //  - HTTP GET on the manifest URL returns empty content.
 //
 // In the case of a payment method manifest download, can also fail when:
+//  - More than three redirects.
+//  - Cross-site redirects.
 //  - HTTP response headers are absent.
 //  - HTTP response headers do not contain Link headers.
 //  - Link header does not contain rel="payment-method-manifest".
-//  - Link header does not contain a valid URL.
+//  - Link header does not contain a valid URL of the same origin.
+//
+// In the case of a web app manifest download, can also also fail when:
+//  - There's a redirect.
 using PaymentManifestDownloadCallback =
-    base::OnceCallback<void(const std::string&)>;
+    base::OnceCallback<void(const GURL& url, const std::string& contents)>;
 
 // Downloader of the payment method manifest and web-app manifest based on the
 // payment method name that is a URL with HTTPS scheme, e.g.,
 // https://bobpay.com.
 //
-// The downloader does not follow redirects. A download succeeds only if all
-// HTTP response codes are 200 or 204.
+// The downloader follows up to three redirects for the HEAD request only (used
+// for payment method manifests). Three is enough for known legitimate use cases
+// and seems like a good upper bound.
 class PaymentManifestDownloader {
  public:
   PaymentManifestDownloader(
@@ -77,20 +85,20 @@
   // 2) GET request for the payment method manifest file.
   //
   // |url| should be a valid URL with HTTPS scheme.
-  virtual void DownloadPaymentMethodManifest(
-      const GURL& url,
-      PaymentManifestDownloadCallback callback);
+  void DownloadPaymentMethodManifest(const GURL& url,
+                                     PaymentManifestDownloadCallback callback);
 
   // Download a web app manifest via a single HTTP request:
   //
   // 1) GET request for the payment method name.
   //
   // |url| should be a valid URL with HTTPS scheme.
-  virtual void DownloadWebAppManifest(const GURL& url,
-                                      PaymentManifestDownloadCallback callback);
+  void DownloadWebAppManifest(const GURL& url,
+                              PaymentManifestDownloadCallback callback);
 
  private:
   friend class PaymentMethodManifestDownloaderTest;
+  friend class TestDownloader;
   friend class WebAppManifestDownloaderTest;
 
   // Information about an ongoing download request.
@@ -129,10 +137,11 @@
   // Called by unittests to get the original URL of the in-progress loader.
   GURL GetLoaderOriginalURLForTesting();
 
-  void InitiateDownload(const GURL& url,
-                        const std::string& method,
-                        int allowed_number_of_redirects,
-                        PaymentManifestDownloadCallback callback);
+  // Overridden in TestDownloader.
+  virtual void InitiateDownload(const GURL& url,
+                                const std::string& method,
+                                int allowed_number_of_redirects,
+                                PaymentManifestDownloadCallback callback);
 
   std::unique_ptr<ErrorLogger> log_;
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
diff --git a/components/payments/core/payment_manifest_downloader_unittest.cc b/components/payments/core/payment_manifest_downloader_unittest.cc
index b390c29..5443e87b 100644
--- a/components/payments/core/payment_manifest_downloader_unittest.cc
+++ b/components/payments/core/payment_manifest_downloader_unittest.cc
@@ -10,12 +10,18 @@
 #include "components/payments/core/error_logger.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace payments {
+namespace {
+
+using testing::_;
+
+}  // namespace
 
 class PaymentMethodManifestDownloaderTest : public testing::Test {
  public:
@@ -34,7 +40,9 @@
 
   ~PaymentMethodManifestDownloaderTest() override {}
 
-  MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
+  MOCK_METHOD2(OnManifestDownload,
+               void(const GURL& unused_url_after_redirects,
+                    const std::string& content));
 
   void CallComplete(int response_code = 200,
                     const std::string& link_header = std::string(),
@@ -48,9 +56,10 @@
 
     if (!link_header.empty())
       headers->AddHeader(link_header);
-    downloader_.OnURLLoaderCompleteInternal(downloader_.GetLoaderForTesting(),
-                                            test_url_, response_body, headers,
-                                            net::OK);
+    downloader_.OnURLLoaderCompleteInternal(
+        downloader_.GetLoaderForTesting(),
+        downloader_.GetLoaderOriginalURLForTesting(), response_body, headers,
+        net::OK);
   }
 
   void CallRedirect(int redirect_code, const GURL& new_url) {
@@ -77,19 +86,19 @@
 };
 
 TEST_F(PaymentMethodManifestDownloaderTest, HttpHeadResponse404IsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(404);
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, NoHttpHeadersIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200, std::string(), std::string(), false);
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpHeaderIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200);
 }
@@ -97,26 +106,26 @@
 TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpLinkHeaderIsFailure) {
   scoped_refptr<net::HttpResponseHeaders> headers(
       new net::HttpResponseHeaders(std::string()));
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200, "Link:");
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, NoRelInHttpLinkHeaderIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200, "Link: <manifest.json>");
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, NoUrlInHttpLinkHeaderIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200, "Link: rel=payment-method-manifest");
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest,
        NoManifestRellInHttpLinkHeaderIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200, "Link: <manifest.json>; rel=web-app-manifest");
 }
@@ -126,7 +135,7 @@
       new net::HttpResponseHeaders(std::string()));
   CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(404);
 }
@@ -134,7 +143,7 @@
 TEST_F(PaymentMethodManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
   CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200, std::string(), std::string(), false);
 }
@@ -142,7 +151,7 @@
 TEST_F(PaymentMethodManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
   CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload(_, "manifest content"));
 
   CallComplete(200, std::string(), "manifest content");
 }
@@ -150,7 +159,7 @@
 TEST_F(PaymentMethodManifestDownloaderTest, HeaderResponseCode204IsSuccess) {
   CallComplete(204, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload(_, "manifest content"));
 
   CallComplete(200, std::string(), "manifest content");
 }
@@ -163,22 +172,22 @@
 
 TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpsHeaderLinkUrl) {
   CallComplete(200,
-               "Link: <https://alicepay.com/manifest.json>; "
+               "Link: <https://bobpay.com/manifest.json>; "
                "rel=payment-method-manifest");
 
-  EXPECT_EQ("https://alicepay.com/manifest.json", GetOriginalURL());
+  EXPECT_EQ("https://bobpay.com/manifest.json", GetOriginalURL());
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, AbsoluteHttpHeaderLinkUrl) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200,
-               "Link: <http://alicepay.com/manifest.json>; "
+               "Link: <http://bobpay.com/manifest.json>; "
                "rel=payment-method-manifest");
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, 300IsUnsupportedRedirect) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallRedirect(300, GURL("https://pay.bobpay.com"));
 }
@@ -194,7 +203,7 @@
 
   CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload(_, "manifest content"));
 
   CallComplete(200, std::string(), "manifest content");
 }
@@ -210,19 +219,19 @@
 
   CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload(_, "manifest content"));
 
   CallComplete(200, std::string(), "manifest content");
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, 304IsUnsupportedRedirect) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallRedirect(304, GURL("https://pay.bobpay.com"));
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, 305IsUnsupportedRedirect) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallRedirect(305, GURL("https://pay.bobpay.com"));
 }
@@ -238,7 +247,7 @@
 
   CallComplete(200, "Link: <manifest.json>; rel=payment-method-manifest");
 
-  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload(_, "manifest content"));
 
   CallComplete(200, std::string(), "manifest content");
 }
@@ -256,19 +265,19 @@
 
   EXPECT_EQ(GetOriginalURL(), GURL("https://newpay.bobpay.com"));
 
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallRedirect(308, GURL("https://newpay.bobpay.com"));
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, InvalidRedirectUrlIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallRedirect(308, GURL("pay.bobpay.com"));
 }
 
 TEST_F(PaymentMethodManifestDownloaderTest, NotAllowCrossSiteRedirects) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallRedirect(301, GURL("https://alicepay.com"));
 }
@@ -290,7 +299,8 @@
 
   ~WebAppManifestDownloaderTest() override {}
 
-  MOCK_METHOD1(OnManifestDownload, void(const std::string& content));
+  MOCK_METHOD2(OnManifestDownload,
+               void(const GURL& url, const std::string& content));
 
   void CallComplete(int response_code,
                     const std::string& response_body = std::string()) {
@@ -313,19 +323,19 @@
 };
 
 TEST_F(WebAppManifestDownloaderTest, HttpGetResponse404IsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(404);
 }
 
 TEST_F(WebAppManifestDownloaderTest, EmptyHttpGetResponseIsFailure) {
-  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+  EXPECT_CALL(*this, OnManifestDownload(_, std::string()));
 
   CallComplete(200);
 }
 
 TEST_F(WebAppManifestDownloaderTest, NonEmptyHttpGetResponseIsSuccess) {
-  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+  EXPECT_CALL(*this, OnManifestDownload(_, "manifest content"));
 
   CallComplete(200, "manifest content");
 }
diff --git a/components/payments/core/test_payment_manifest_downloader.cc b/components/payments/core/test_payment_manifest_downloader.cc
index a14b37e..319ef30 100644
--- a/components/payments/core/test_payment_manifest_downloader.cc
+++ b/components/payments/core/test_payment_manifest_downloader.cc
@@ -21,42 +21,36 @@
 
 TestDownloader::~TestDownloader() {}
 
-void TestDownloader::DownloadPaymentMethodManifest(
-    const GURL& url,
-    PaymentManifestDownloadCallback callback) {
-  PaymentManifestDownloader::DownloadPaymentMethodManifest(
-      FindTestServerURL(url), std::move(callback));
-}
-
-void TestDownloader::DownloadWebAppManifest(
-    const GURL& url,
-    PaymentManifestDownloadCallback callback) {
-  PaymentManifestDownloader::DownloadWebAppManifest(FindTestServerURL(url),
-                                                    std::move(callback));
-}
-
 void TestDownloader::AddTestServerURL(const std::string& prefix,
                                       const GURL& test_server_url) {
   test_server_url_[prefix] = test_server_url;
 }
 
-GURL TestDownloader::FindTestServerURL(const GURL& url) const {
-  GURL actual_url = url;
+void TestDownloader::InitiateDownload(
+    const GURL& url,
+    const std::string& method,
+    int allowed_number_of_redirects,
+    PaymentManifestDownloadCallback callback) {
+  PaymentManifestDownloader::InitiateDownload(FindTestServerURL(url), method,
+                                              allowed_number_of_redirects,
+                                              std::move(callback));
+}
 
+GURL TestDownloader::FindTestServerURL(const GURL& url) const {
   // Find the first key in |test_server_url_| that is a prefix of |url|. If
   // found, then replace this prefix in the |url| with the URL of the test
   // server that should be used instead.
   for (const auto& prefix_and_url : test_server_url_) {
     const std::string& prefix = prefix_and_url.first;
     const GURL& test_server_url = prefix_and_url.second;
-    if (base::StartsWith(url.spec(), prefix, base::CompareCase::SENSITIVE)) {
-      actual_url =
-          GURL(test_server_url.spec() + url.spec().substr(prefix.length()));
-      break;
+    if (base::StartsWith(url.spec(), prefix, base::CompareCase::SENSITIVE) &&
+        !base::StartsWith(url.spec(), test_server_url.spec(),
+                          base::CompareCase::SENSITIVE)) {
+      return GURL(test_server_url.spec() + url.spec().substr(prefix.length()));
     }
   }
 
-  return actual_url;
+  return url;
 }
 
 }  // namespace payments
diff --git a/components/payments/core/test_payment_manifest_downloader.h b/components/payments/core/test_payment_manifest_downloader.h
index da0aafd..7c1a51a 100644
--- a/components/payments/core/test_payment_manifest_downloader.h
+++ b/components/payments/core/test_payment_manifest_downloader.h
@@ -52,14 +52,6 @@
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~TestDownloader() override;
 
-  // PaymentManifestDownloader implementation.
-  void DownloadPaymentMethodManifest(
-      const GURL& url,
-      PaymentManifestDownloadCallback callback) override;
-  void DownloadWebAppManifest(
-      const GURL& url,
-      PaymentManifestDownloadCallback callback) override;
-
   // Modifies the downloader to replace all instances of |prefix| with
   // |test_server_url| when downloading payment method manifests and web app
   // manifests.
@@ -96,6 +88,12 @@
   void AddTestServerURL(const std::string& prefix, const GURL& test_server_url);
 
  private:
+  // PaymentManifestDownloader implementation.
+  void InitiateDownload(const GURL& url,
+                        const std::string& method,
+                        int allowed_number_of_redirects,
+                        PaymentManifestDownloadCallback callback) override;
+
   GURL FindTestServerURL(const GURL& url) const;
 
   // The mapping from the URL prefix to the URL of the test server to be used.
diff --git a/components/search_provider_logos/google_logo_api_unittest.cc b/components/search_provider_logos/google_logo_api_unittest.cc
index 38e2189..1e63d72 100644
--- a/components/search_provider_logos/google_logo_api_unittest.cc
+++ b/components/search_provider_logos/google_logo_api_unittest.cc
@@ -371,6 +371,7 @@
   EXPECT_EQ(LogoType::INTERACTIVE, logo->metadata.type);
   EXPECT_EQ(500, logo->metadata.iframe_width_px);
   EXPECT_EQ(200, logo->metadata.iframe_height_px);
+  EXPECT_EQ(nullptr, logo->encoded_image);
 }
 
 TEST(GoogleNewLogoApiTest, ParsesInteractiveDoodleWithNewWindowAsSimple) {
diff --git a/components/search_provider_logos/logo_cache.cc b/components/search_provider_logos/logo_cache.cc
index a06f4019..f350805 100644
--- a/components/search_provider_logos/logo_cache.cc
+++ b/components/search_provider_logos/logo_cache.cc
@@ -35,6 +35,8 @@
 const char kLogUrlKey[] = "log_url";
 const char kCtaLogUrlKey[] = "cta_log_url";
 const char kShortLinkKey[] = "short_link";
+const char kIframeWidthPx[] = "iframe_width_px";
+const char kIframeHeightPx[] = "iframe_height_px";
 
 const char kShareButtonX[] = "share_button_x";
 const char kShareButtonY[] = "share_button_y";
@@ -124,13 +126,16 @@
 
 void LogoCache::SetCachedLogo(const EncodedLogo* logo) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::unique_ptr<LogoMetadata> metadata;
-  if (logo) {
-    metadata = std::make_unique<LogoMetadata>(logo->metadata);
-    logo_num_bytes_ = static_cast<int>(logo->encoded_image->size());
+  if (!logo) {
+    UpdateMetadata(nullptr);
+    DeleteLogoAndMetadata();
+    return;
   }
-  UpdateMetadata(std::move(metadata));
-  WriteLogo(logo ? logo->encoded_image : nullptr);
+
+  logo_num_bytes_ =
+      logo->encoded_image ? static_cast<int>(logo->encoded_image->size()) : 0;
+  UpdateMetadata(std::make_unique<LogoMetadata>(logo->metadata));
+  WriteLogo(logo->encoded_image);
 }
 
 std::unique_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
@@ -140,18 +145,21 @@
   if (!metadata_)
     return nullptr;
 
-  scoped_refptr<base::RefCountedString> encoded_image =
-      new base::RefCountedString();
-  if (!base::ReadFileToString(GetLogoPath(), &encoded_image->data())) {
-    UpdateMetadata(nullptr);
-    return nullptr;
-  }
+  scoped_refptr<base::RefCountedString> encoded_image;
+  if (logo_num_bytes_ != 0) {
+    encoded_image = new base::RefCountedString();
 
-  if (encoded_image->size() != static_cast<size_t>(logo_num_bytes_)) {
-    // Delete corrupt metadata and logo.
-    DeleteLogoAndMetadata();
-    UpdateMetadata(nullptr);
-    return nullptr;
+    if (!base::ReadFileToString(GetLogoPath(), &encoded_image->data())) {
+      UpdateMetadata(nullptr);
+      return nullptr;
+    }
+
+    if (encoded_image->size() != static_cast<size_t>(logo_num_bytes_)) {
+      // Delete corrupt metadata and logo.
+      DeleteLogoAndMetadata();
+      UpdateMetadata(nullptr);
+      return nullptr;
+    }
   }
 
   std::unique_ptr<EncodedLogo> logo(new EncodedLogo());
@@ -197,6 +205,8 @@
       !dict->GetDouble(kShareButtonOpacity, &metadata->share_button_opacity) ||
       !dict->GetString(kShareButtonIcon, &metadata->share_button_icon) ||
       !dict->GetString(kShareButtonBg, &metadata->share_button_bg) ||
+      !dict->GetInteger(kIframeWidthPx, &metadata->iframe_width_px) ||
+      !dict->GetInteger(kIframeHeightPx, &metadata->iframe_height_px) ||
       !GetTimeValue(*dict, kExpirationTimeKey, &metadata->expiration_time)) {
     return nullptr;
   }
@@ -236,6 +246,8 @@
   dict.SetDouble(kShareButtonOpacity, metadata.share_button_opacity);
   dict.SetString(kShareButtonIcon, metadata.share_button_icon);
   dict.SetString(kShareButtonBg, metadata.share_button_bg);
+  dict.SetInteger(kIframeWidthPx, metadata.iframe_width_px);
+  dict.SetInteger(kIframeHeightPx, metadata.iframe_height_px);
   SetTimeValue(dict, kExpirationTimeKey, metadata.expiration_time);
   base::JSONWriter::Write(dict, str);
 }
@@ -284,7 +296,7 @@
   if (!EnsureCacheDirectoryExists())
     return;
 
-  if (!metadata_ || !encoded_image) {
+  if (!metadata_) {
     DeleteLogoAndMetadata();
     return;
   }
@@ -298,10 +310,9 @@
   if (!base::DeleteFile(metadata_path, false))
     return;
 
-  if (base::WriteFile(
-          logo_path,
-          encoded_image->front_as<char>(),
-          static_cast<int>(encoded_image->size())) == -1) {
+  if (encoded_image &&
+      base::WriteFile(logo_path, encoded_image->front_as<char>(),
+                      static_cast<int>(encoded_image->size())) == -1) {
     base::DeleteFile(logo_path, false);
     return;
   }
diff --git a/components/search_provider_logos/logo_cache_unittest.cc b/components/search_provider_logos/logo_cache_unittest.cc
index e8122f5..82714fd6 100644
--- a/components/search_provider_logos/logo_cache_unittest.cc
+++ b/components/search_provider_logos/logo_cache_unittest.cc
@@ -82,6 +82,13 @@
   return logo;
 }
 
+std::unique_ptr<EncodedLogo> GetExampleLogoWithoutImage() {
+  auto logo = std::make_unique<EncodedLogo>();
+  logo->encoded_image = nullptr;
+  logo->metadata = GetExampleMetadata2();
+  return logo;
+}
+
 void ExpectMetadataEqual(const LogoMetadata& expected_metadata,
                          const LogoMetadata& actual_metadata) {
   EXPECT_EQ(expected_metadata.source_url, actual_metadata.source_url);
@@ -155,6 +162,14 @@
     }
   }
 
+  void ExpectLogoWithoutImage(const EncodedLogo* expected_logo) {
+    std::unique_ptr<EncodedLogo> retrieved_logo(cache_->GetCachedLogo());
+    ASSERT_TRUE(retrieved_logo.get());
+    ASSERT_FALSE(retrieved_logo->encoded_image.get());
+    ASSERT_FALSE(expected_logo->encoded_image.get());
+    ExpectMetadataEqual(expected_logo->metadata, retrieved_logo->metadata);
+  }
+
   // Deletes the existing LogoCache and creates a new one. This clears any
   // logo or metadata cached in memory to simulate restarting Chrome.
   void SimulateRestart() {
@@ -244,6 +259,33 @@
   ExpectLogo(logo.get());
 }
 
+TEST_F(LogoCacheTest, StoreAndRetrieveLogoWithoutImage) {
+  // Expect no metadata at first.
+  ExpectLogo(nullptr);
+
+  // Set initial logo.
+  std::unique_ptr<EncodedLogo> logo = GetExampleLogoWithoutImage();
+  cache_->SetCachedLogo(logo.get());
+  ExpectLogoWithoutImage(logo.get());
+
+  // Update logo to null.
+  cache_->SetCachedLogo(nullptr);
+  ExpectLogo(nullptr);
+
+  // Read logo back from disk.
+  SimulateRestart();
+  ExpectLogo(nullptr);
+
+  // Update logo.
+  logo = GetExampleLogoWithoutImage();
+  cache_->SetCachedLogo(logo.get());
+  ExpectLogoWithoutImage(logo.get());
+
+  // Read logo back from disk.
+  SimulateRestart();
+  ExpectLogoWithoutImage(logo.get());
+}
+
 TEST_F(LogoCacheTest, RetrieveCorruptMetadata) {
   // Set initial logo.
   std::unique_ptr<EncodedLogo> logo = GetExampleLogo2();
diff --git a/components/search_provider_logos/logo_service_impl.cc b/components/search_provider_logos/logo_service_impl.cc
index 105e8d9..f8295a38 100644
--- a/components/search_provider_logos/logo_service_impl.cc
+++ b/components/search_provider_logos/logo_service_impl.cc
@@ -391,7 +391,7 @@
     std::unique_ptr<EncodedLogo> cached_logo) {
   DCHECK(!is_idle_);
 
-  if (cached_logo) {
+  if (cached_logo && cached_logo->encoded_image) {
     // Store the value of logo->encoded_image for use below. This ensures that
     // logo->encoded_image is evaulated before base::Passed(&logo), which sets
     // logo to NULL.
@@ -402,6 +402,8 @@
         ImageDecodedHandlerWithTimeout::Wrap(base::BindRepeating(
             &LogoServiceImpl::OnCachedLogoAvailable,
             weak_ptr_factory_.GetWeakPtr(), base::Passed(&cached_logo))));
+  } else if (cached_logo) {
+    OnCachedLogoAvailable(std::move(cached_logo), SkBitmap());
   } else {
     OnCachedLogoAvailable({}, SkBitmap());
   }
@@ -425,7 +427,7 @@
     const SkBitmap& image) {
   DCHECK(!is_idle_);
 
-  if (!image.isNull()) {
+  if (encoded_logo && encoded_logo->encoded_image && !image.isNull()) {
     cached_logo_.reset(new Logo());
     cached_logo_->metadata = encoded_logo->metadata;
     cached_logo_->image = image;
@@ -533,15 +535,24 @@
     encoded_logo->metadata.mime_type = cached_logo_->metadata.mime_type;
     SetCachedMetadata(encoded_logo->metadata);
     download_outcome = DOWNLOAD_OUTCOME_LOGO_REVALIDATED;
-  } else if (encoded_logo && image.isNull()) {
+  } else if (encoded_logo && encoded_logo->encoded_image && image.isNull()) {
     // Image decoding failed. Do nothing.
     download_outcome = DOWNLOAD_OUTCOME_DECODING_FAILED;
+  } else if (encoded_logo && !encoded_logo->encoded_image &&
+             encoded_logo->metadata.type != LogoType::INTERACTIVE) {
+    download_outcome = DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE;
+#if defined(OS_ANDROID) || defined(OS_IOS)
+  } else if (encoded_logo && !encoded_logo->encoded_image) {
+    // On Mobile interactive doodles require a static CTA image, on Desktop the
+    // static image is not required as it's handled by the iframed page.
+    download_outcome = DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE;
+#endif
   } else {
     // Check if the server returned a valid, non-empty response.
     if (encoded_logo) {
       UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", from_http_cache);
 
-      DCHECK(!image.isNull());
+      DCHECK(!encoded_logo->encoded_image || !image.isNull());
       logo.reset(new Logo());
       logo->metadata = encoded_logo->metadata;
       logo->image = image;
@@ -578,6 +589,7 @@
       }
       break;
 
+    case DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE:
     case DOWNLOAD_OUTCOME_DOWNLOAD_FAILED:
       // In the download failed, don't notify the callback at all, since the
       // callback should continue to use the cached logo.
diff --git a/components/search_provider_logos/logo_service_impl.h b/components/search_provider_logos/logo_service_impl.h
index ca1e89f..b3fb983 100644
--- a/components/search_provider_logos/logo_service_impl.h
+++ b/components/search_provider_logos/logo_service_impl.h
@@ -88,7 +88,7 @@
 
  private:
   // These values must stay in sync with the NewTabPageLogoDownloadOutcome enum
-  // in histograms.xml. And any addtion should be treated as append-only!
+  // in enums.xml. And any addition should be treated as append-only!
   // Animated doodle is not covered by this enum.
   enum LogoDownloadOutcome {
     DOWNLOAD_OUTCOME_NEW_LOGO_SUCCESS,
@@ -97,6 +97,7 @@
     DOWNLOAD_OUTCOME_PARSING_FAILED,
     DOWNLOAD_OUTCOME_DECODING_FAILED,
     DOWNLOAD_OUTCOME_LOGO_REVALIDATED,
+    DOWNLOAD_OUTCOME_MISSING_REQUIRED_IMAGE,
     DOWNLOAD_OUTCOME_COUNT,
   };
 
diff --git a/components/test/data/payments/charlie.example.com/webpay b/components/test/data/payments/charlie.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/charlie.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/charlie.example.com/webpay.mock-http-headers b/components/test/data/payments/charlie.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..01006d5
--- /dev/null
+++ b/components/test/data/payments/charlie.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 301 Moved Permanently
+Location: https://david.example.com/webpay
diff --git a/components/test/data/payments/david.example.com/webpay b/components/test/data/payments/david.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/david.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/david.example.com/webpay.mock-http-headers b/components/test/data/payments/david.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..b6f27509
--- /dev/null
+++ b/components/test/data/payments/david.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 301 Moved Permanently
+Location: https://frank.example.com/webpay
diff --git a/components/test/data/payments/frank.example.com/webpay b/components/test/data/payments/frank.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/frank.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/frank.example.com/webpay.mock-http-headers b/components/test/data/payments/frank.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..c1800e85
--- /dev/null
+++ b/components/test/data/payments/frank.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 301 Moved Permanently
+Location: https://george.example.com/webpay
diff --git a/components/test/data/payments/george.example.com/webpay b/components/test/data/payments/george.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/george.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/george.example.com/webpay.mock-http-headers b/components/test/data/payments/george.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..dfd055af
--- /dev/null
+++ b/components/test/data/payments/george.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 301 Moved Permanently
+Location: https://harry.example.com/webpay
diff --git a/components/test/data/payments/harry.example.com/app.js b/components/test/data/payments/harry.example.com/app.js
new file mode 100644
index 0000000..856a895
--- /dev/null
+++ b/components/test/data/payments/harry.example.com/app.js
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ */
+
+self.addEventListener('canmakepayment', (evt) => {
+  evt.respondWith(true);
+});
+
+self.addEventListener('paymentrequest', (evt) => {
+  evt.respondWith({
+    methodName: evt.methodData[0].supportedMethods,
+    details: {transactionId: '123'},
+  });
+});
diff --git a/components/test/data/payments/harry.example.com/app.json b/components/test/data/payments/harry.example.com/app.json
new file mode 100644
index 0000000..dcd056b9
--- /dev/null
+++ b/components/test/data/payments/harry.example.com/app.json
@@ -0,0 +1,14 @@
+{
+  "name": "Pay with Harry.Example",
+  "short_name": "Harry.Example",
+  "icons": [{
+    "src": "icon.png",
+    "sizes": "48x48",
+    "type": "image/png"
+  }],
+  "serviceworker": {
+    "src": "app.js",
+    "use_cache": false,
+    "scope": "/webpay"
+  }
+}
diff --git a/components/test/data/payments/harry.example.com/payment-manifest.json b/components/test/data/payments/harry.example.com/payment-manifest.json
new file mode 100644
index 0000000..08c1cce
--- /dev/null
+++ b/components/test/data/payments/harry.example.com/payment-manifest.json
@@ -0,0 +1,3 @@
+{
+  "default_applications": ["https://harry.example.com/app.json"]
+}
diff --git a/components/test/data/payments/harry.example.com/webpay b/components/test/data/payments/harry.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/harry.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/harry.example.com/webpay.mock-http-headers b/components/test/data/payments/harry.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..44e5a707
--- /dev/null
+++ b/components/test/data/payments/harry.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Link: <payment-manifest.json>; rel="payment-method-manifest"
diff --git a/components/test/data/payments/ike.example.com/webpay b/components/test/data/payments/ike.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/ike.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/ike.example.com/webpay.mock-http-headers b/components/test/data/payments/ike.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..914b10f
--- /dev/null
+++ b/components/test/data/payments/ike.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Link: <https://harry.example.com/payment-manifest.json>; rel="payment-method-manifest"
diff --git a/components/test/data/payments/john.example.com/payment-manifest.json b/components/test/data/payments/john.example.com/payment-manifest.json
new file mode 100644
index 0000000..08c1cce
--- /dev/null
+++ b/components/test/data/payments/john.example.com/payment-manifest.json
@@ -0,0 +1,3 @@
+{
+  "default_applications": ["https://harry.example.com/app.json"]
+}
diff --git a/components/test/data/payments/john.example.com/webpay b/components/test/data/payments/john.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/john.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/john.example.com/webpay.mock-http-headers b/components/test/data/payments/john.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..44e5a707
--- /dev/null
+++ b/components/test/data/payments/john.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Link: <payment-manifest.json>; rel="payment-method-manifest"
diff --git a/components/test/data/payments/kyle.example.com/app.json b/components/test/data/payments/kyle.example.com/app.json
new file mode 100644
index 0000000..af200754
--- /dev/null
+++ b/components/test/data/payments/kyle.example.com/app.json
@@ -0,0 +1,14 @@
+{
+  "name": "Pay with Kyle.Example",
+  "short_name": "Kyle.Example",
+  "icons": [{
+    "src": "icon.png",
+    "sizes": "48x48",
+    "type": "image/png"
+  }],
+  "serviceworker": {
+    "src": "https://harry.example.com/app.js",
+    "use_cache": false,
+    "scope": "/webpay"
+  }
+}
diff --git a/components/test/data/payments/kyle.example.com/payment-manifest.json b/components/test/data/payments/kyle.example.com/payment-manifest.json
new file mode 100644
index 0000000..38ec952
--- /dev/null
+++ b/components/test/data/payments/kyle.example.com/payment-manifest.json
@@ -0,0 +1,3 @@
+{
+  "default_applications": ["https://kyle.example.com/app.json"]
+}
diff --git a/components/test/data/payments/kyle.example.com/webpay b/components/test/data/payments/kyle.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/kyle.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/kyle.example.com/webpay.mock-http-headers b/components/test/data/payments/kyle.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..44e5a707
--- /dev/null
+++ b/components/test/data/payments/kyle.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Link: <payment-manifest.json>; rel="payment-method-manifest"
diff --git a/components/test/data/payments/larry.example.com/app.js b/components/test/data/payments/larry.example.com/app.js
new file mode 100644
index 0000000..856a895
--- /dev/null
+++ b/components/test/data/payments/larry.example.com/app.js
@@ -0,0 +1,16 @@
+/*
+ * 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.
+ */
+
+self.addEventListener('canmakepayment', (evt) => {
+  evt.respondWith(true);
+});
+
+self.addEventListener('paymentrequest', (evt) => {
+  evt.respondWith({
+    methodName: evt.methodData[0].supportedMethods,
+    details: {transactionId: '123'},
+  });
+});
diff --git a/components/test/data/payments/larry.example.com/app.json b/components/test/data/payments/larry.example.com/app.json
new file mode 100644
index 0000000..0feae0e
--- /dev/null
+++ b/components/test/data/payments/larry.example.com/app.json
@@ -0,0 +1,14 @@
+{
+  "name": "Pay with Larry.Example",
+  "short_name": "Larry.Example",
+  "icons": [{
+    "src": "icon.png",
+    "sizes": "48x48",
+    "type": "image/png"
+  }],
+  "serviceworker": {
+    "src": "app.js",
+    "use_cache": false,
+    "scope": "https://harry.example.com/webpay"
+  }
+}
diff --git a/components/test/data/payments/larry.example.com/payment-manifest.json b/components/test/data/payments/larry.example.com/payment-manifest.json
new file mode 100644
index 0000000..7f4e8d7
--- /dev/null
+++ b/components/test/data/payments/larry.example.com/payment-manifest.json
@@ -0,0 +1,3 @@
+{
+  "default_applications": ["https://larry.example.com/app.json"]
+}
diff --git a/components/test/data/payments/larry.example.com/webpay b/components/test/data/payments/larry.example.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/larry.example.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/larry.example.com/webpay.mock-http-headers b/components/test/data/payments/larry.example.com/webpay.mock-http-headers
new file mode 100644
index 0000000..44e5a707
--- /dev/null
+++ b/components/test/data/payments/larry.example.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Link: <payment-manifest.json>; rel="payment-method-manifest"
diff --git a/components/test/data/payments/larrypay.com/webpay b/components/test/data/payments/larrypay.com/webpay
new file mode 100644
index 0000000..e0dfe7d
--- /dev/null
+++ b/components/test/data/payments/larrypay.com/webpay
@@ -0,0 +1 @@
+<!-- Intentionally blank. -->
diff --git a/components/test/data/payments/larrypay.com/webpay.mock-http-headers b/components/test/data/payments/larrypay.com/webpay.mock-http-headers
new file mode 100644
index 0000000..cd5a3346f
--- /dev/null
+++ b/components/test/data/payments/larrypay.com/webpay.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 301 Moved Permanently
+Location: https://kylepay.com/webpay
diff --git a/components/translate/content/browser/BUILD.gn b/components/translate/content/browser/BUILD.gn
index efe80df1..dca8ea5a 100644
--- a/components/translate/content/browser/BUILD.gn
+++ b/components/translate/content/browser/BUILD.gn
@@ -17,6 +17,7 @@
     "//components/translate/core/common",
   ]
   deps = [
+    "//components/language/core/browser",
     "//content/public/browser",
     "//content/public/common",
     "//net",
diff --git a/components/translate/content/browser/content_translate_driver.cc b/components/translate/content/browser/content_translate_driver.cc
index 945ec30..e3ee164 100644
--- a/components/translate/content/browser/content_translate_driver.cc
+++ b/components/translate/content/browser/content_translate_driver.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/language/core/browser/url_language_histogram.h"
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "content/public/browser/browser_context.h"
@@ -34,12 +35,14 @@
 namespace translate {
 
 ContentTranslateDriver::ContentTranslateDriver(
-    content::NavigationController* nav_controller)
+    content::NavigationController* nav_controller,
+    language::UrlLanguageHistogram* url_language_histogram)
     : content::WebContentsObserver(nav_controller->GetWebContents()),
       navigation_controller_(nav_controller),
       translate_manager_(nullptr),
       max_reload_check_attempts_(kMaxTranslateLoadCheckAttempts),
       next_page_seq_no_(0),
+      language_histogram_(url_language_histogram),
       weak_pointer_factory_(this) {
   DCHECK(navigation_controller_);
 }
@@ -236,10 +239,20 @@
   pages_.erase(page_seq_no);
 }
 
-void ContentTranslateDriver::OnPageReady(
-    mojom::PagePtr page,
-    const LanguageDetectionDetails& details,
-    bool page_needs_translation) {
+void ContentTranslateDriver::AddBinding(
+    translate::mojom::ContentTranslateDriverRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void ContentTranslateDriver::RegisterPage(
+    translate::mojom::PagePtr page,
+    const translate::LanguageDetectionDetails& details,
+    const bool page_needs_translation) {
+  // If we have a language histogram (i.e. we're not in incognito), update it
+  // with the detected language of every page visited.
+  if (language_histogram_ && details.is_cld_reliable)
+    language_histogram_->OnPageVisited(details.cld_language);
+
   pages_[++next_page_seq_no_] = std::move(page);
   pages_[next_page_seq_no_].set_connection_error_handler(
       base::BindOnce(&ContentTranslateDriver::OnPageAway,
diff --git a/components/translate/content/browser/content_translate_driver.h b/components/translate/content/browser/content_translate_driver.h
index 1843cd3..24f5034 100644
--- a/components/translate/content/browser/content_translate_driver.h
+++ b/components/translate/content/browser/content_translate_driver.h
@@ -14,12 +14,17 @@
 #include "components/translate/core/browser/translate_driver.h"
 #include "components/translate/core/common/translate_errors.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
 
 namespace content {
 class NavigationController;
 class WebContents;
 }
 
+namespace language {
+class UrlLanguageHistogram;
+}  // namespace language
+
 namespace translate {
 
 struct LanguageDetectionDetails;
@@ -27,6 +32,7 @@
 
 // Content implementation of TranslateDriver.
 class ContentTranslateDriver : public TranslateDriver,
+                               public translate::mojom::ContentTranslateDriver,
                                public content::WebContentsObserver {
  public:
   // The observer for the ContentTranslateDriver.
@@ -52,8 +58,9 @@
     virtual ~Observer() {}
   };
 
-  explicit ContentTranslateDriver(
-      content::NavigationController* nav_controller);
+  ContentTranslateDriver(
+      content::NavigationController* nav_controller,
+      language::UrlLanguageHistogram* url_language_histogram);
   ~ContentTranslateDriver() override;
 
   // Adds or Removes observers.
@@ -100,10 +107,12 @@
                         const std::string& translated_lang,
                         TranslateErrors::Type error_type);
 
+  // Adds a binding in |bindings_| for the passed |request|.
+  void AddBinding(translate::mojom::ContentTranslateDriverRequest request);
   // Called when a page has been loaded and can be potentially translated.
-  void OnPageReady(mojom::PagePtr page,
-                   const LanguageDetectionDetails& details,
-                   bool page_needs_translation);
+  void RegisterPage(translate::mojom::PagePtr page,
+                    const translate::LanguageDetectionDetails& details,
+                    bool page_needs_translation) override;
 
  private:
   void OnPageAway(int page_seq_no);
@@ -120,8 +129,20 @@
 
   // Records mojo connections with all current alive pages.
   int next_page_seq_no_;
+  // PagePtr is the connection between this driver and a TranslateHelper (which
+  // are per RenderFrame). Each TranslateHelper has a |binding_| member,
+  // representing the other end of this pipe.
   std::map<int, mojom::PagePtr> pages_;
 
+  // Histogram to be notified about detected language of every page visited. Not
+  // owned here.
+  language::UrlLanguageHistogram* const language_histogram_;
+
+  // ContentTranslateDriver is a singleton per web contents but multiple render
+  // frames may be contained in a single web contents. TranslateHelpers get the
+  // other end of this binding in the form of a ContentTranslateDriverPtr.
+  mojo::BindingSet<translate::mojom::ContentTranslateDriver> bindings_;
+
   base::WeakPtrFactory<ContentTranslateDriver> weak_pointer_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ContentTranslateDriver);
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 4e89dd1..f0c077d 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -28,7 +28,7 @@
 // (OOP-D).
 // TODO(dnicoara): Look at enabling Chromecast support when ChromeOS support is
 // ready.
-#if defined(OS_CHROMEOS) || defined(OS_FUCHSIA) || defined(IS_CHROMECAST)
+#if defined(OS_CHROMEOS) || defined(IS_CHROMECAST)
 const base::Feature kVizDisplayCompositor{"VizDisplayCompositor",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 #else
diff --git a/components/viz/common/skia_helper.cc b/components/viz/common/skia_helper.cc
index eef0203..2228ca37 100644
--- a/components/viz/common/skia_helper.cc
+++ b/components/viz/common/skia_helper.cc
@@ -9,7 +9,8 @@
 #include "ui/gfx/skia_util.h"
 
 namespace viz {
-sk_sp<SkImage> SkiaHelper::ApplyImageFilter(sk_sp<SkImage> src_image,
+sk_sp<SkImage> SkiaHelper::ApplyImageFilter(GrContext* context,
+                                            sk_sp<SkImage> src_image,
                                             const gfx::RectF& src_rect,
                                             const gfx::RectF& dst_rect,
                                             const gfx::Vector2dF& scale,
@@ -42,8 +43,8 @@
   filter = filter->makeWithLocalMatrix(local_matrix);
   SkIRect in_subset = SkIRect::MakeWH(src_rect.width(), src_rect.height());
 
-  sk_sp<SkImage> image = src_image->makeWithFilter(filter.get(), in_subset,
-                                                   clip_bounds, subset, offset);
+  sk_sp<SkImage> image = src_image->makeWithFilter(
+      context, filter.get(), in_subset, clip_bounds, subset, offset);
   if (!image || !image->isTextureBacked()) {
     return nullptr;
   }
diff --git a/components/viz/common/skia_helper.h b/components/viz/common/skia_helper.h
index d4bab0a..8aefe8f 100644
--- a/components/viz/common/skia_helper.h
+++ b/components/viz/common/skia_helper.h
@@ -17,7 +17,8 @@
 class VIZ_COMMON_EXPORT SkiaHelper {
  public:
   // |flush| is necessary for GLRenderer but not SkiaRenderer.
-  static sk_sp<SkImage> ApplyImageFilter(sk_sp<SkImage> src_image,
+  static sk_sp<SkImage> ApplyImageFilter(GrContext* context,
+                                         sk_sp<SkImage> src_image,
                                          const gfx::RectF& src_rect,
                                          const gfx::RectF& dst_rect,
                                          const gfx::Vector2dF& scale,
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index f9c47dd..6ee04d0 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -910,8 +910,9 @@
   SkIPoint offset;
   SkIRect subset;
   sk_sp<SkImage> filtered_image = SkiaHelper::ApplyImageFilter(
-      src_image, src_image_rect, src_image_rect, gfx::Vector2dF(1, 1),
-      std::move(filter), &offset, &subset, quad->filters_origin, true);
+      use_gr_context->context(), src_image, src_image_rect, src_image_rect,
+      gfx::Vector2dF(1, 1), std::move(filter), &offset, &subset,
+      quad->filters_origin, true);
 
   if (!backdrop_filter_bounds.IsEmpty()) {
     // Clip the filtered image to the bounding box of the element.
@@ -1215,8 +1216,9 @@
                           params->contents_texture->size(),
                           use_gr_context->context(), params->flip_texture);
           params->filter_image = SkiaHelper::ApplyImageFilter(
-              src_image, src_rect, params->dst_rect, quad->filters_scale,
-              std::move(filter), &offset, &subset, quad->filters_origin, true);
+              use_gr_context->context(), src_image, src_rect, params->dst_rect,
+              quad->filters_scale, std::move(filter), &offset, &subset,
+              quad->filters_origin, true);
         } else {
           DisplayResourceProvider::ScopedReadLockGL
               prefilter_bypass_quad_texture_lock(
@@ -1229,8 +1231,9 @@
                           prefilter_bypass_quad_texture_lock.size(),
                           use_gr_context->context(), params->flip_texture);
           params->filter_image = SkiaHelper::ApplyImageFilter(
-              src_image, src_rect, params->dst_rect, quad->filters_scale,
-              std::move(filter), &offset, &subset, quad->filters_origin, true);
+              use_gr_context->context(), src_image, src_rect, params->dst_rect,
+              quad->filters_scale, std::move(filter), &offset, &subset,
+              quad->filters_origin, true);
         }
 
         if (!params->filter_image)
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index ff7a201..bca9aca 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -959,8 +959,9 @@
     gfx::RectF src_rect(quad->rect);
     // TODO(xing.xu): Support flip_texture. (https://crbug.com/822859)
     params->filter_image = SkiaHelper::ApplyImageFilter(
-        content, src_rect, dst_rect, quad->filters_scale, std::move(filter),
-        &offset, &subset, quad->filters_origin, false);
+        current_canvas_->getGrContext(), content, src_rect, dst_rect,
+        quad->filters_scale, std::move(filter), &offset, &subset,
+        quad->filters_origin, false);
     if (!params->filter_image)
       return false;
     params->dst_rect =
diff --git a/content/browser/appcache/OWNERS b/content/browser/appcache/OWNERS
index db1043c..37049f1 100644
--- a/content/browser/appcache/OWNERS
+++ b/content/browser/appcache/OWNERS
@@ -1,3 +1,7 @@
+# Primary
+mek@chromium.org
+
+# Secondary
 pwnall@chromium.org
 jsbell@chromium.org
 
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 2b832c85..25a0ecd 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1385,9 +1385,7 @@
                                return entry.origin == origin;
                              });
       if (it != matching_origins.end()) {
-        // TODO(alexmos): Simplify this by defining operator <= for IdType.
-        DCHECK(it->min_browsing_instance_id < min_browsing_instance_id ||
-               it->min_browsing_instance_id == min_browsing_instance_id);
+        DCHECK_LE(it->min_browsing_instance_id, min_browsing_instance_id);
         continue;
       }
     }
@@ -1462,12 +1460,9 @@
   bool found = false;
   if (it != isolated_origins_.end()) {
     for (const auto& isolated_origin_entry : it->second) {
-      // TODO(alexmos): Simplify this by defining operator <= for IdType.
       bool matches_browsing_instance_id =
-          isolated_origin_entry.min_browsing_instance_id <
-              browsing_instance_id ||
-          isolated_origin_entry.min_browsing_instance_id ==
-              browsing_instance_id;
+          isolated_origin_entry.min_browsing_instance_id <=
+          browsing_instance_id;
       if (matches_browsing_instance_id &&
           IsolatedOriginUtil::DoesOriginMatchIsolatedOrigin(
               origin, isolated_origin_entry.origin)) {
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index 5ba72edd2..789c406 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -441,10 +441,8 @@
 
 void RenderFrameMessageFilter::OnCreateChildFrame(
     const FrameHostMsg_CreateChildFrame_Params& params,
-    int* new_routing_id,
-    FrameHostMsg_CreateChildFrame_Params_Reply* params_reply,
-    base::UnguessableToken* devtools_frame_token) {
-  *new_routing_id = render_widget_helper_->GetNextRoutingID();
+    FrameHostMsg_CreateChildFrame_Params_Reply* params_reply) {
+  params_reply->child_routing_id = render_widget_helper_->GetNextRoutingID();
 
   service_manager::mojom::InterfaceProviderPtr interface_provider;
   auto interface_provider_request(mojo::MakeRequest(&interface_provider));
@@ -464,16 +462,16 @@
   params_reply->document_interface_broker_blink_handle =
       document_interface_broker_blink.PassHandle().release();
 
-  *devtools_frame_token = base::UnguessableToken::Create();
+  params_reply->devtools_frame_token = base::UnguessableToken::Create();
 
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(
           &CreateChildFrameOnUI, render_process_id_, params.parent_routing_id,
           params.scope, params.frame_name, params.frame_unique_name,
-          params.is_created_by_script, *devtools_frame_token,
+          params.is_created_by_script, params_reply->devtools_frame_token,
           params.frame_policy, params.frame_owner_properties,
-          params.frame_owner_element_type, *new_routing_id,
+          params.frame_owner_element_type, params_reply->child_routing_id,
           interface_provider_request.PassMessagePipe(),
           document_interface_broker_request_content.PassMessagePipe(),
           document_interface_broker_request_blink.PassMessagePipe()));
diff --git a/content/browser/frame_host/render_frame_message_filter.h b/content/browser/frame_host/render_frame_message_filter.h
index da3f345..5d68724 100644
--- a/content/browser/frame_host/render_frame_message_filter.h
+++ b/content/browser/frame_host/render_frame_message_filter.h
@@ -101,14 +101,11 @@
   void InitializeCookieManager(
       network::mojom::CookieManagerRequest cookie_manager_request);
 
-  // |new_render_frame_id|, |document_interface_broker_content_handle|,
-  // |document_interface_broker_blink_handle| and |devtools_frame_token| are out
-  // parameters. Browser process defines them for the renderer process.
+  // |params_reply| is an out parameter. Browser process defines it for the
+  // renderer process.
   void OnCreateChildFrame(
       const FrameHostMsg_CreateChildFrame_Params& params,
-      int* new_render_frame_id,
-      FrameHostMsg_CreateChildFrame_Params_Reply* params_reply,
-      base::UnguessableToken* devtools_frame_token);
+      FrameHostMsg_CreateChildFrame_Params_Reply* params_reply);
   void OnCookiesEnabled(int render_frame_id,
                         const GURL& url,
                         const GURL& site_for_cookies,
diff --git a/content/browser/loader/resource_message_filter.cc b/content/browser/loader/resource_message_filter.cc
index d873292..1b92bdb 100644
--- a/content/browser/loader/resource_message_filter.cc
+++ b/content/browser/loader/resource_message_filter.cc
@@ -195,7 +195,8 @@
         base::BindRepeating(&ResourceDispatcherHostImpl::CancelRequest,
                             base::Unretained(ResourceDispatcherHostImpl::Get()),
                             requester_info_->child_id()),
-        &shared_cors_origin_access_list_->GetOriginAccessList());
+        &shared_cors_origin_access_list_->GetOriginAccessList(),
+        requester_info_->child_id() == -1 ? 0 : requester_info_->child_id());
   }
 
   std::vector<network::mojom::URLLoaderFactoryRequest> requests =
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
index 1adb32ac..c29ca87 100644
--- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
+++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -195,8 +195,6 @@
   // When last_event_time of the end_component is less than the first_event_time
   // of the start_component, zero is recorded instead of a negative value.
   histogram_tester().ExpectUniqueSample(
-      "Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin2", 0, 1);
-  histogram_tester().ExpectUniqueSample(
       "Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin4", 0, 1);
   histogram_tester().ExpectUniqueSample(
       "Event.Latency.Scroll.Wheel.TimeToScrollUpdateSwapBegin2", 0, 1);
@@ -261,10 +259,6 @@
                           0));
       EXPECT_TRUE(
           HistogramSizeEq("Event.Latency.ScrollBegin.Wheel."
-                          "TimeToScrollUpdateSwapBegin2",
-                          1));
-      EXPECT_TRUE(
-          HistogramSizeEq("Event.Latency.ScrollBegin.Wheel."
                           "TimeToScrollUpdateSwapBegin4",
                           1));
       EXPECT_TRUE(
@@ -273,10 +267,6 @@
                           1));
       EXPECT_TRUE(
           HistogramSizeEq("Event.Latency.ScrollUpdate.Wheel."
-                          "TimeToScrollUpdateSwapBegin2",
-                          0));
-      EXPECT_TRUE(
-          HistogramSizeEq("Event.Latency.ScrollUpdate.Wheel."
                           "TimeToScrollUpdateSwapBegin4",
                           0));
       EXPECT_TRUE(
@@ -374,10 +364,6 @@
                           1));
       EXPECT_TRUE(
           HistogramSizeEq("Event.Latency.ScrollBegin.Wheel."
-                          "TimeToScrollUpdateSwapBegin2",
-                          0));
-      EXPECT_TRUE(
-          HistogramSizeEq("Event.Latency.ScrollBegin.Wheel."
                           "TimeToScrollUpdateSwapBegin4",
                           0));
       EXPECT_TRUE(
@@ -386,10 +372,6 @@
                           1));
       EXPECT_TRUE(
           HistogramSizeEq("Event.Latency.ScrollUpdate.Wheel."
-                          "TimeToScrollUpdateSwapBegin2",
-                          1));
-      EXPECT_TRUE(
-          HistogramSizeEq("Event.Latency.ScrollUpdate.Wheel."
                           "TimeToScrollUpdateSwapBegin4",
                           1));
 
@@ -468,10 +450,6 @@
     // UMA histograms.
     EXPECT_TRUE(
         HistogramSizeEq("Event.Latency.ScrollInertial.Touch."
-                        "TimeToScrollUpdateSwapBegin2",
-                        1));
-    EXPECT_TRUE(
-        HistogramSizeEq("Event.Latency.ScrollInertial.Touch."
                         "TimeToScrollUpdateSwapBegin4",
                         1));
     EXPECT_TRUE(HistogramSizeEq(
@@ -563,13 +541,9 @@
                         "TimeToScrollUpdateSwapBegin2",
                         0));
     EXPECT_TRUE(HistogramSizeEq(
-        "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2", 1));
-    EXPECT_TRUE(HistogramSizeEq(
         "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin4", 1));
 
     EXPECT_TRUE(HistogramSizeEq(
-        "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2", 0));
-    EXPECT_TRUE(HistogramSizeEq(
         "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin4", 0));
 
     EXPECT_TRUE(
@@ -676,12 +650,8 @@
                         "TimeToScrollUpdateSwapBegin2",
                         1));
     EXPECT_TRUE(HistogramSizeEq(
-        "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2", 0));
-    EXPECT_TRUE(HistogramSizeEq(
         "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin4", 0));
     EXPECT_TRUE(HistogramSizeEq(
-        "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2", 1));
-    EXPECT_TRUE(HistogramSizeEq(
         "Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin4", 1));
     EXPECT_TRUE(HistogramSizeEq(
         "Event.Latency.ScrollBegin.Touch.TimeToHandled2_Main", 0));
diff --git a/content/browser/renderer_host/input/scroll_latency_browsertest.cc b/content/browser/renderer_host/input/scroll_latency_browsertest.cc
index 5be1c7dd..5f808f6 100644
--- a/content/browser/renderer_host/input/scroll_latency_browsertest.cc
+++ b/content/browser/renderer_host/input/scroll_latency_browsertest.cc
@@ -144,7 +144,7 @@
 
   DoSmoothWheelScroll(gfx::Vector2d(0, 100));
   while (!VerifyRecordedSamplesForHistogram(
-      1, "Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin2")) {
+      1, "Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin4")) {
     GiveItSomeTime();
     FetchHistogramsFromChildProcesses();
   }
@@ -198,7 +198,7 @@
   EXPECT_TRUE(VerifyRecordedSamplesForHistogram(
       0, "Event.Latency.ScrollBegin.Touch.TimeToHandled2_Impl"));
   EXPECT_TRUE(VerifyRecordedSamplesForHistogram(
-      0, "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2"));
+      0, "Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin4"));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 8ff54d7b..95710d31 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -345,6 +345,9 @@
 
     case WebInputEvent::kGestureTapDown:
       gesture_sequence_in_progress_ = true;
+      // TODO(xidachen): investigate why the touch action has no value.
+      if (compositor_touch_action_enabled_ && !touch_action.has_value())
+        SetTouchAction(cc::kTouchActionAuto);
       // In theory, the num_of_active_touches_ should be > 0 at this point. But
       // crash reports suggest otherwise.
       if (num_of_active_touches_ <= 0)
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index b2654d4..0e34f92 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -11005,7 +11005,7 @@
   ack_waiter.Wait();
 
   // Make sure all the page scale values behave as expected.
-  const float kScaleTolerance = 0.0001f;
+  const float kScaleTolerance = 0.05f;
   observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
   observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
   observer_c.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index d48e8196..6ffaaf08 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -671,11 +671,13 @@
 IPC_STRUCT_END()
 
 IPC_STRUCT_BEGIN(FrameHostMsg_CreateChildFrame_Params_Reply)
+  IPC_STRUCT_MEMBER(int32_t, child_routing_id)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, new_interface_provider)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle,
                     document_interface_broker_content_handle)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle,
                     document_interface_broker_blink_handle)
+  IPC_STRUCT_MEMBER(base::UnguessableToken, devtools_frame_token)
 IPC_STRUCT_END()
 
 IPC_STRUCT_TRAITS_BEGIN(content::CSPSource)
@@ -1166,18 +1168,13 @@
 //
 // Each of these messages will have a corresponding FrameHostMsg_Detach message
 // sent when the frame is detached from the DOM.
-// Note that |new_routing_id|, |params_reply|, and |devtools_frame_token| are
-// out parameters. Browser process defines them for the renderer process.
-// TODO(crbug.com/718652): move |new_routing_id| and |devtools_frame_token| into
-// FrameHostMsg_CreateChildFrame_Params_Reply.
-IPC_SYNC_MESSAGE_CONTROL1_3(FrameHostMsg_CreateChildFrame,
+// Note that |params_reply| is an out parameter. Browser process defines it for
+// the renderer process.
+// |params_reply.child_routing_id| may not be assigned MSG_ROUTING_NONE.
+IPC_SYNC_MESSAGE_CONTROL1_1(FrameHostMsg_CreateChildFrame,
                             FrameHostMsg_CreateChildFrame_Params,
-                            // new_routing_id
-                            int32_t,
                             // params_reply
-                            FrameHostMsg_CreateChildFrame_Params_Reply,
-                            // devtools_frame_token
-                            base::UnguessableToken)
+                            FrameHostMsg_CreateChildFrame_Params_Reply)
 
 // Sent by the renderer to the parent RenderFrameHost when a child frame is
 // detached from the DOM.
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/BrowserTaskExecutor.java b/content/public/android/java/src/org/chromium/content_public/browser/BrowserTaskExecutor.java
index ee555e2..22c0aaa 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/BrowserTaskExecutor.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/BrowserTaskExecutor.java
@@ -52,8 +52,8 @@
     }
 
     @Override
-    public void postTask(TaskTraits taskTraits, Runnable task) {
-        createSingleThreadTaskRunner(taskTraits).postTask(task);
+    public void postDelayedTask(TaskTraits taskTraits, Runnable task, long delay) {
+        createSingleThreadTaskRunner(taskTraits).postDelayedTask(task, delay);
     }
 
     public static void register() {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
index ac2f6df..2b85f7f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/NativePostTaskTest.java
@@ -12,6 +12,7 @@
 import android.support.test.filters.MediumTest;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -78,11 +79,58 @@
 
     @Test
     @MediumTest
+    public void testNativePostDelayedTask() throws Exception {
+        final Object lock = new Object();
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        PostTask.postDelayedTask(new TaskTraits(), new Runnable() {
+            @Override
+            public void run() {
+                synchronized (lock) {
+                    taskExecuted.set(true);
+                    lock.notify();
+                }
+            }
+        }, 1);
+
+        // We verify that the task didn't get scheduled before the native scheduler is initialised
+        Assert.assertFalse(taskExecuted.get());
+        startNativeScheduler();
+
+        try {
+            // The task should now be scheduled at some point after the delay, so the test shouldn't
+            // time out.
+            synchronized (lock) {
+                while (!taskExecuted.get()) {
+                    lock.wait();
+                }
+            }
+        } catch (InterruptedException ie) {
+            ie.printStackTrace();
+        }
+    }
+
+    @Test
+    @MediumTest
     public void testCreateTaskRunner() throws Exception {
         startNativeScheduler();
         TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
         // This should not time out.
+        SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1);
+    }
+
+    private void testRunningTasksInSequence(TaskRunner taskQueue) {
+        List<Integer> orderListImmediate = new ArrayList<>();
+        List<Integer> orderListDelayed = new ArrayList<>();
+
+        SchedulerTestHelpers.postThreeTasksInOrder(taskQueue, orderListImmediate);
         SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+
+        assertThat(orderListImmediate, contains(1, 2, 3));
+
+        SchedulerTestHelpers.postThreeDelayedTasksInOrder(taskQueue, orderListDelayed);
+        SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1);
+
+        assertThat(orderListDelayed, contains(1, 2, 3));
     }
 
     @Test
@@ -90,13 +138,7 @@
     public void testCreateSequencedTaskRunner() throws Exception {
         startNativeScheduler();
         TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
-
-        assertThat(orderList, contains(1, 2, 3));
+        testRunningTasksInSequence(taskQueue);
     }
 
     @Test
@@ -104,64 +146,80 @@
     public void testCreateSingleThreadSequencedTaskRunner() throws Exception {
         startNativeScheduler();
         TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 2);
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 3);
-        SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue);
+        testRunningTasksInSequence(taskQueue);
+    }
 
-        assertThat(orderList, contains(1, 2, 3));
+    private void performSequencedTestSchedulerMigration(TaskRunner taskQueue,
+            List<Integer> orderListImmediate, List<Integer> orderListDelayed) throws Exception {
+        SchedulerTestHelpers.postThreeTasksInOrder(taskQueue, orderListImmediate);
+        SchedulerTestHelpers.postThreeDelayedTasksInOrder(taskQueue, orderListDelayed);
+
+        postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
+            @Override
+            public void run() {
+                orderListImmediate.add(4);
+            }
+        });
+        // We wait until all the delayed tasks have been scheduled.
+        SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1);
     }
 
     @Test
     @MediumTest
     public void testCreateTaskRunnerMigrationToNative() throws Exception {
-        TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
+        final Object lock = new Object();
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
 
-        postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
+        taskQueue.postDelayedTask(new Runnable() {
             @Override
             public void run() {
-                orderList.add(2);
+                synchronized (lock) {
+                    taskExecuted.set(true);
+                    lock.notify();
+                }
             }
-        });
+        }, 1);
 
-        assertThat(orderList, contains(1, 2));
+        // We verify that the task didn't get scheduled before the native scheduler is initialised
+        Assert.assertFalse(taskExecuted.get());
+        startNativeScheduler();
+
+        try {
+            // The task should now be scheduled at some point after the delay, so the test shouldn't
+            // time out.
+            synchronized (lock) {
+                while (!taskExecuted.get()) {
+                    lock.wait();
+                }
+            }
+        } catch (InterruptedException ie) {
+            ie.printStackTrace();
+        }
     }
 
     @Test
     @MediumTest
     public void testCreateSequencedTaskRunnerMigrationToNative() throws Exception {
+        List<Integer> orderListImmediate = new ArrayList<>();
+        List<Integer> orderListDelayed = new ArrayList<>();
         TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
+        performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed);
 
-        postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
-            @Override
-            public void run() {
-                orderList.add(2);
-            }
-        });
-
-        assertThat(orderList, contains(1, 2));
+        assertThat(orderListImmediate, contains(1, 2, 3, 4));
+        assertThat(orderListDelayed, contains(1, 2, 3));
     }
 
     @Test
     @MediumTest
     public void testCreateSingleThreadSequencedTaskRunnerMigrationToNative() throws Exception {
+        List<Integer> orderListImmediate = new ArrayList<>();
+        List<Integer> orderListDelayed = new ArrayList<>();
         TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits());
-        List<Integer> orderList = new ArrayList<>();
-        SchedulerTestHelpers.postRecordOrderTask(taskQueue, orderList, 1);
+        performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed);
 
-        postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
-            @Override
-            public void run() {
-                orderList.add(2);
-            }
-        });
-
-        assertThat(orderList, contains(1, 2));
+        assertThat(orderListImmediate, contains(1, 2, 3, 4));
+        assertThat(orderListDelayed, contains(1, 2, 3));
     }
 
     private void postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index be24618..9a02b44 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -2368,7 +2368,7 @@
     const float tolerance) {
   while (std::abs(render_frame_metadata_provider_->LastRenderFrameMetadata()
                       .page_scale_factor -
-                  expected_page_scale_factor) < tolerance) {
+                  expected_page_scale_factor) > tolerance) {
     WaitForMetadataChange();
   }
 }
@@ -2378,7 +2378,7 @@
     const float tolerance) {
   while (std::abs(render_frame_metadata_provider_->LastRenderFrameMetadata()
                       .external_page_scale_factor -
-                  expected_external_page_scale_factor) < tolerance) {
+                  expected_external_page_scale_factor) > tolerance) {
     WaitForMetadataChange();
   }
 }
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index c0869e4..3b94a8b7 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -299,19 +299,18 @@
 // The Frame expects to be returned a valid route_id different from its own.
 void MockRenderThread::OnCreateChildFrame(
     const FrameHostMsg_CreateChildFrame_Params& params,
-    int* new_render_frame_id,
-    FrameHostMsg_CreateChildFrame_Params_Reply* params_reply,
-    base::UnguessableToken* devtools_frame_token) {
-  *new_render_frame_id = GetNextRoutingID();
+    FrameHostMsg_CreateChildFrame_Params_Reply* params_reply) {
+  params_reply->child_routing_id = GetNextRoutingID();
   service_manager::mojom::InterfaceProviderPtr interface_provider;
   frame_routing_id_to_initial_interface_provider_requests_.emplace(
-      *new_render_frame_id, mojo::MakeRequest(&interface_provider));
+      params_reply->child_routing_id, mojo::MakeRequest(&interface_provider));
   params_reply->new_interface_provider =
       interface_provider.PassInterface().PassHandle().release();
 
   blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker;
   frame_routing_id_to_initial_document_broker_requests_.emplace(
-      *new_render_frame_id, mojo::MakeRequest(&document_interface_broker));
+      params_reply->child_routing_id,
+      mojo::MakeRequest(&document_interface_broker));
   params_reply->document_interface_broker_content_handle =
       document_interface_broker.PassInterface().PassHandle().release();
 
@@ -320,7 +319,7 @@
   params_reply->document_interface_broker_blink_handle =
       document_interface_broker_blink.PassInterface().PassHandle().release();
 
-  *devtools_frame_token = base::UnguessableToken::Create();
+  params_reply->devtools_frame_token = base::UnguessableToken::Create();
 }
 
 bool MockRenderThread::OnControlMessageReceived(const IPC::Message& msg) {
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h
index 8b80021..acec3326 100644
--- a/content/public/test/mock_render_thread.h
+++ b/content/public/test/mock_render_thread.h
@@ -140,9 +140,7 @@
   // The Frame expects to be returned a valid route_id different from its own.
   void OnCreateChildFrame(
       const FrameHostMsg_CreateChildFrame_Params& params,
-      int* new_render_frame_id,
-      FrameHostMsg_CreateChildFrame_Params_Reply* params_reply,
-      base::UnguessableToken* devtools_frame_token);
+      FrameHostMsg_CreateChildFrame_Params_Reply* params_reply);
 
 #if defined(OS_WIN)
   void OnDuplicateSection(base::SharedMemoryHandle renderer_handle,
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 78422e0..b5698e5 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -287,8 +287,6 @@
     "media/stream/media_stream_registry_interface.h",
     "media/stream/media_stream_renderer_factory_impl.cc",
     "media/stream/media_stream_renderer_factory_impl.h",
-    "media/stream/media_stream_track.cc",
-    "media/stream/media_stream_track.h",
     "media/stream/media_stream_types.h",
     "media/stream/media_stream_video_capturer_source.cc",
     "media/stream/media_stream_video_capturer_source.h",
diff --git a/content/renderer/image_capture/image_capture_frame_grabber.cc b/content/renderer/image_capture/image_capture_frame_grabber.cc
index 58c58f0..1b22b48 100644
--- a/content/renderer/image_capture/image_capture_frame_grabber.cc
+++ b/content/renderer/image_capture/image_capture_frame_grabber.cc
@@ -119,7 +119,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!!callbacks);
 
-  DCHECK(track && !track->IsNull() && track->GetTrackData());
+  DCHECK(track && !track->IsNull() && track->GetPlatformTrack());
   DCHECK_EQ(blink::WebMediaStreamSource::kTypeVideo, track->Source().GetType());
 
   if (frame_grab_in_progress_) {
diff --git a/content/renderer/media/stream/media_stream_audio_source.cc b/content/renderer/media/stream/media_stream_audio_source.cc
index 79d9d9b6..b224ff5 100644
--- a/content/renderer/media/stream/media_stream_audio_source.cc
+++ b/content/renderer/media/stream/media_stream_audio_source.cc
@@ -70,8 +70,8 @@
   // Create and initialize a new MediaStreamAudioTrack and pass ownership of it
   // to the WebMediaStreamTrack.
   blink::WebMediaStreamTrack mutable_blink_track = blink_track;
-  mutable_blink_track.SetTrackData(
-      CreateMediaStreamAudioTrack(blink_track.Id().Utf8()).release());
+  mutable_blink_track.SetPlatformTrack(
+      CreateMediaStreamAudioTrack(blink_track.Id().Utf8()));
 
   // Propagate initial "enabled" state.
   MediaStreamAudioTrack* const track = MediaStreamAudioTrack::From(blink_track);
diff --git a/content/renderer/media/stream/media_stream_audio_track.cc b/content/renderer/media/stream/media_stream_audio_track.cc
index d520b2e..39bfee67 100644
--- a/content/renderer/media/stream/media_stream_audio_track.cc
+++ b/content/renderer/media/stream/media_stream_audio_track.cc
@@ -16,7 +16,9 @@
 namespace content {
 
 MediaStreamAudioTrack::MediaStreamAudioTrack(bool is_local_track)
-    : MediaStreamTrack(is_local_track), is_enabled_(1), weak_factory_(this) {
+    : blink::PlatformMediaStreamTrack(is_local_track),
+      is_enabled_(1),
+      weak_factory_(this) {
   DVLOG(1) << "MediaStreamAudioTrack@" << this << "::MediaStreamAudioTrack("
            << (is_local_track ? "local" : "remote") << " track)";
 }
@@ -34,7 +36,7 @@
       track.Source().GetType() != blink::WebMediaStreamSource::kTypeAudio) {
     return nullptr;
   }
-  return static_cast<MediaStreamAudioTrack*>(track.GetTrackData());
+  return static_cast<MediaStreamAudioTrack*>(track.GetPlatformTrack());
 }
 
 void MediaStreamAudioTrack::AddSink(MediaStreamAudioSink* sink) {
diff --git a/content/renderer/media/stream/media_stream_audio_track.h b/content/renderer/media/stream/media_stream_audio_track.h
index 1e7fb0c..3bbea1c 100644
--- a/content/renderer/media/stream/media_stream_audio_track.h
+++ b/content/renderer/media/stream/media_stream_audio_track.h
@@ -13,8 +13,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
+#include "content/common/content_export.h"
 #include "content/renderer/media/stream/media_stream_audio_deliverer.h"
-#include "content/renderer/media/stream/media_stream_track.h"
+#include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
 
 namespace content {
 
@@ -25,7 +26,8 @@
 // MediaStreamAudioSource to one or more MediaStreamAudioSinks. An instance of
 // this class is owned by blink::WebMediaStreamTrack, and clients should use
 // From() to gain access to a MediaStreamAudioTrack.
-class CONTENT_EXPORT MediaStreamAudioTrack : public MediaStreamTrack {
+class CONTENT_EXPORT MediaStreamAudioTrack
+    : public blink::PlatformMediaStreamTrack {
  public:
   explicit MediaStreamAudioTrack(bool is_local_track);
 
diff --git a/content/renderer/media/stream/media_stream_center.cc b/content/renderer/media/stream/media_stream_center.cc
index 45f5213..16f6ca82 100644
--- a/content/renderer/media/stream/media_stream_center.cc
+++ b/content/renderer/media/stream/media_stream_center.cc
@@ -74,13 +74,13 @@
 }
 
 void CreateNativeVideoMediaStreamTrack(blink::WebMediaStreamTrack track) {
-  DCHECK(track.GetTrackData() == nullptr);
+  DCHECK(track.GetPlatformTrack() == nullptr);
   blink::WebMediaStreamSource source = track.Source();
   DCHECK_EQ(source.GetType(), blink::WebMediaStreamSource::kTypeVideo);
   MediaStreamVideoSource* native_source =
       MediaStreamVideoSource::GetVideoSource(source);
   DCHECK(native_source);
-  track.SetTrackData(new MediaStreamVideoTrack(
+  track.SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
       native_source, MediaStreamVideoSource::ConstraintsCallback(),
       track.IsEnabled()));
 }
@@ -88,7 +88,7 @@
 void CloneNativeVideoMediaStreamTrack(
     const blink::WebMediaStreamTrack& original,
     blink::WebMediaStreamTrack clone) {
-  DCHECK(!clone.GetTrackData());
+  DCHECK(!clone.GetPlatformTrack());
   blink::WebMediaStreamSource source = clone.Source();
   DCHECK_EQ(source.GetType(), blink::WebMediaStreamSource::kTypeVideo);
   MediaStreamVideoSource* native_source =
@@ -97,7 +97,7 @@
   MediaStreamVideoTrack* original_track =
       MediaStreamVideoTrack::GetVideoTrack(original);
   DCHECK(original_track);
-  clone.SetTrackData(new MediaStreamVideoTrack(
+  clone.SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
       native_source, original_track->adapter_settings(),
       original_track->noise_reduction(), original_track->is_screencast(),
       original_track->min_frame_rate(),
@@ -113,7 +113,7 @@
 void MediaStreamCenter::DidCreateMediaStreamTrack(
     const blink::WebMediaStreamTrack& track) {
   DVLOG(1) << "MediaStreamCenter::didCreateMediaStreamTrack";
-  DCHECK(!track.IsNull() && !track.GetTrackData());
+  DCHECK(!track.IsNull() && !track.GetPlatformTrack());
   DCHECK(!track.Source().IsNull());
 
   switch (track.Source().GetType()) {
@@ -130,7 +130,7 @@
     const blink::WebMediaStreamTrack& original,
     const blink::WebMediaStreamTrack& clone) {
   DCHECK(!clone.IsNull());
-  DCHECK(!clone.GetTrackData());
+  DCHECK(!clone.GetPlatformTrack());
   DCHECK(!clone.Source().IsNull());
 
   switch (clone.Source().GetType()) {
@@ -145,23 +145,24 @@
 
 void MediaStreamCenter::DidSetContentHint(
     const blink::WebMediaStreamTrack& track) {
-  MediaStreamTrack* native_track = MediaStreamTrack::GetTrack(track);
+  blink::PlatformMediaStreamTrack* native_track =
+      blink::PlatformMediaStreamTrack::GetTrack(track);
   if (native_track)
     native_track->SetContentHint(track.ContentHint());
 }
 
 void MediaStreamCenter::DidEnableMediaStreamTrack(
     const blink::WebMediaStreamTrack& track) {
-  MediaStreamTrack* native_track =
-      MediaStreamTrack::GetTrack(track);
+  blink::PlatformMediaStreamTrack* native_track =
+      blink::PlatformMediaStreamTrack::GetTrack(track);
   if (native_track)
     native_track->SetEnabled(true);
 }
 
 void MediaStreamCenter::DidDisableMediaStreamTrack(
     const blink::WebMediaStreamTrack& track) {
-  MediaStreamTrack* native_track =
-      MediaStreamTrack::GetTrack(track);
+  blink::PlatformMediaStreamTrack* native_track =
+      blink::PlatformMediaStreamTrack::GetTrack(track);
   if (native_track)
     native_track->SetEnabled(false);
 }
@@ -170,8 +171,8 @@
 MediaStreamCenter::CreateWebAudioSourceFromMediaStreamTrack(
     const blink::WebMediaStreamTrack& track) {
   DVLOG(1) << "MediaStreamCenter::createWebAudioSourceFromMediaStreamTrack";
-  MediaStreamTrack* media_stream_track =
-      static_cast<MediaStreamTrack*>(track.GetTrackData());
+  blink::PlatformMediaStreamTrack* media_stream_track =
+      track.GetPlatformTrack();
   if (!media_stream_track) {
     DLOG(ERROR) << "Native track missing for webaudio source.";
     return nullptr;
diff --git a/content/renderer/media/stream/media_stream_track.cc b/content/renderer/media/stream/media_stream_track.cc
deleted file mode 100644
index c1635fe..0000000
--- a/content/renderer/media/stream/media_stream_track.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2014 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/renderer/media/stream/media_stream_track.h"
-
-namespace content {
-
-// static
-MediaStreamTrack* MediaStreamTrack::GetTrack(
-    const blink::WebMediaStreamTrack& track) {
-  return track.IsNull() ? nullptr
-                        : static_cast<MediaStreamTrack*>(track.GetTrackData());
-}
-
-MediaStreamTrack::MediaStreamTrack(bool is_local_track)
-    : is_local_track_(is_local_track) {
-}
-
-MediaStreamTrack::~MediaStreamTrack() {
-}
-
-}  // namespace content
diff --git a/content/renderer/media/stream/media_stream_track.h b/content/renderer/media/stream/media_stream_track.h
deleted file mode 100644
index 911b93bf..0000000
--- a/content/renderer/media/stream/media_stream_track.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2014 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_RENDERER_MEDIA_STREAM_MEDIA_STREAM_TRACK_H_
-#define CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_TRACK_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/threading/thread_checker.h"
-#include "content/common/content_export.h"
-#include "media/base/audio_parameters.h"
-#include "third_party/blink/public/platform/web_media_stream_track.h"
-
-namespace content {
-
-// MediaStreamTrack is a Chrome representation of blink::WebMediaStreamTrack.
-// It is owned by blink::WebMediaStreamTrack as
-// blink::WebMediaStreamTrack::ExtraData.
-class CONTENT_EXPORT MediaStreamTrack
-    : public blink::WebMediaStreamTrack::TrackData {
- public:
-  explicit MediaStreamTrack(bool is_local_track);
-  ~MediaStreamTrack() override;
-
-  static MediaStreamTrack* GetTrack(const blink::WebMediaStreamTrack& track);
-
-  virtual void SetEnabled(bool enabled) = 0;
-
-  virtual void SetContentHint(
-      blink::WebMediaStreamTrack::ContentHintType content_hint) = 0;
-
-  // If |callback| is not null, it is invoked when the track has stopped.
-  virtual void StopAndNotify(base::OnceClosure callback) = 0;
-
-  void Stop() { StopAndNotify(base::OnceClosure()); }
-
-  // TODO(hta): Make method pure virtual when all tracks have the method.
-  void GetSettings(blink::WebMediaStreamTrack::Settings& settings) override {}
-
-  bool is_local_track() const { return is_local_track_; }
-
- protected:
-  const bool is_local_track_;
-
-  // Used to DCHECK that we are called on Render main Thread.
-  THREAD_CHECKER(main_render_thread_checker_);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MediaStreamTrack);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_MEDIA_STREAM_MEDIA_STREAM_TRACK_H_
diff --git a/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc b/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc
index bf6aabb..f79604a8 100644
--- a/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc
+++ b/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc
@@ -153,6 +153,8 @@
 
   void OnSourceStopped(const blink::WebMediaStreamSource& source) {
     source_stopped_ = true;
+    if (source.IsNull())
+      return;
     EXPECT_EQ(source.Id(), webkit_source_id_);
   }
   void OnStarted(bool result) {
@@ -355,7 +357,8 @@
       .WillOnce(InvokeWithoutArgs(
           this, &MediaStreamVideoCapturerSourceTest::SetStopCaptureFlag));
   EXPECT_CALL(*this, MockNotification());
-  MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
+  blink::PlatformMediaStreamTrack* track =
+      blink::PlatformMediaStreamTrack::GetTrack(web_track);
   track->StopAndNotify(
       base::BindOnce(&MediaStreamVideoCapturerSourceTest::MockNotification,
                      base::Unretained(this)));
diff --git a/content/renderer/media/stream/media_stream_video_source.cc b/content/renderer/media/stream/media_stream_video_source.cc
index 41ad9d2..80d6051 100644
--- a/content/renderer/media/stream/media_stream_video_source.cc
+++ b/content/renderer/media/stream/media_stream_video_source.cc
@@ -157,8 +157,6 @@
                                            RestartResult result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(callback);
-  DCHECK_EQ(Owner().GetReadyState(),
-            blink::WebMediaStreamSource::kReadyStateEnded);
   if (result == RestartResult::IS_STOPPED) {
     state_ = ENDED;
   }
diff --git a/content/renderer/media/stream/media_stream_video_source_unittest.cc b/content/renderer/media/stream/media_stream_video_source_unittest.cc
index ca458bc..eed9cd5f 100644
--- a/content/renderer/media/stream/media_stream_video_source_unittest.cc
+++ b/content/renderer/media/stream/media_stream_video_source_unittest.cc
@@ -652,7 +652,8 @@
             blink::WebMediaStreamSource::kReadyStateLive);
 
   EXPECT_CALL(*this, MockNotification());
-  MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
+  blink::PlatformMediaStreamTrack* track =
+      blink::PlatformMediaStreamTrack::GetTrack(web_track);
   track->StopAndNotify(base::BindOnce(
       &MediaStreamVideoSourceTest::MockNotification, base::Unretained(this)));
   EXPECT_EQ(web_track.Source().GetReadyState(),
@@ -669,7 +670,8 @@
             blink::WebMediaStreamSource::kReadyStateLive);
 
   EXPECT_CALL(*this, MockNotification());
-  MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
+  blink::PlatformMediaStreamTrack* track =
+      blink::PlatformMediaStreamTrack::GetTrack(web_track);
   track->StopAndNotify(base::BindOnce(
       &MediaStreamVideoSourceTest::MockNotification, base::Unretained(this)));
   EXPECT_EQ(web_track.Source().GetReadyState(),
diff --git a/content/renderer/media/stream/media_stream_video_track.cc b/content/renderer/media/stream/media_stream_video_track.cc
index 98ca0e7..bfe1996 100644
--- a/content/renderer/media/stream/media_stream_video_track.cc
+++ b/content/renderer/media/stream/media_stream_video_track.cc
@@ -207,7 +207,8 @@
     bool enabled) {
   blink::WebMediaStreamTrack track;
   track.Initialize(source->Owner());
-  track.SetTrackData(new MediaStreamVideoTrack(source, callback, enabled));
+  track.SetPlatformTrack(
+      std::make_unique<MediaStreamVideoTrack>(source, callback, enabled));
   return track;
 }
 
@@ -219,7 +220,8 @@
     bool enabled) {
   blink::WebMediaStreamTrack track;
   track.Initialize(id, source->Owner());
-  track.SetTrackData(new MediaStreamVideoTrack(source, callback, enabled));
+  track.SetPlatformTrack(
+      std::make_unique<MediaStreamVideoTrack>(source, callback, enabled));
   return track;
 }
 
@@ -234,7 +236,7 @@
     bool enabled) {
   blink::WebMediaStreamTrack track;
   track.Initialize(source->Owner());
-  track.SetTrackData(new MediaStreamVideoTrack(
+  track.SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
       source, adapter_settings, noise_reduction, is_screencast, min_frame_rate,
       callback, enabled));
   return track;
@@ -247,14 +249,14 @@
       track.Source().GetType() != blink::WebMediaStreamSource::kTypeVideo) {
     return nullptr;
   }
-  return static_cast<MediaStreamVideoTrack*>(track.GetTrackData());
+  return static_cast<MediaStreamVideoTrack*>(track.GetPlatformTrack());
 }
 
 MediaStreamVideoTrack::MediaStreamVideoTrack(
     MediaStreamVideoSource* source,
     const MediaStreamVideoSource::ConstraintsCallback& callback,
     bool enabled)
-    : MediaStreamTrack(true),
+    : PlatformMediaStreamTrack(true),
       frame_deliverer_(
           new MediaStreamVideoTrack::FrameDeliverer(source->io_task_runner(),
                                                     enabled)),
@@ -284,7 +286,7 @@
     const base::Optional<double>& min_frame_rate,
     const MediaStreamVideoSource::ConstraintsCallback& callback,
     bool enabled)
-    : MediaStreamTrack(true),
+    : PlatformMediaStreamTrack(true),
       frame_deliverer_(
           new MediaStreamVideoTrack::FrameDeliverer(source->io_task_runner(),
                                                     enabled)),
diff --git a/content/renderer/media/stream/media_stream_video_track.h b/content/renderer/media/stream/media_stream_video_track.h
index 97530ee8..3d0d2034 100644
--- a/content/renderer/media/stream/media_stream_video_track.h
+++ b/content/renderer/media/stream/media_stream_video_track.h
@@ -15,9 +15,9 @@
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
 #include "content/public/renderer/media_stream_video_sink.h"
-#include "content/renderer/media/stream/media_stream_track.h"
 #include "content/renderer/media/stream/media_stream_video_source.h"
 #include "content/renderer/media/stream/secure_display_link_tracker.h"
+#include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 
 namespace content {
@@ -28,7 +28,8 @@
 // blink::WebMediaStreamTrack in content. It is owned by the blink object
 // and can be retrieved from a blink object using
 // WebMediaStreamTrack::getExtraData() or MediaStreamVideoTrack::GetVideoTrack.
-class CONTENT_EXPORT MediaStreamVideoTrack : public MediaStreamTrack {
+class CONTENT_EXPORT MediaStreamVideoTrack
+    : public blink::PlatformMediaStreamTrack {
  public:
   // Help method to create a blink::WebMediaStreamTrack and a
   // MediaStreamVideoTrack instance. The MediaStreamVideoTrack object is owned
@@ -151,6 +152,10 @@
                bool is_sink_secure);
   void RemoveSink(MediaStreamVideoSink* sink);
 
+  // In debug builds, check that all methods that could cause object graph
+  // or data flow changes are being called on the main thread.
+  THREAD_CHECKER(main_render_thread_checker_);
+
   std::vector<MediaStreamVideoSink*> sinks_;
 
   // |FrameDeliverer| is an internal helper object used for delivering video
diff --git a/content/renderer/media/stream/mock_media_stream_registry.cc b/content/renderer/media/stream/mock_media_stream_registry.cc
index cb0a866..3c47ae7e 100644
--- a/content/renderer/media/stream/mock_media_stream_registry.cc
+++ b/content/renderer/media/stream/mock_media_stream_registry.cc
@@ -68,11 +68,10 @@
   blink::WebMediaStreamTrack blink_track;
   blink_track.Initialize(blink::WebString::FromUTF8(track_id), blink_source);
 
-  MediaStreamVideoTrack* native_track = new MediaStreamVideoTrack(
+  blink_track.SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
       native_source, adapter_settings, noise_reduction, is_screencast,
       min_frame_rate, MediaStreamVideoSource::ConstraintsCallback(),
-      true /* enabled */);
-  blink_track.SetTrackData(native_track);
+      true /* enabled */));
   test_stream_.AddTrack(blink_track);
 }
 
diff --git a/content/renderer/media/stream/remote_media_stream_track_adapter.cc b/content/renderer/media/stream/remote_media_stream_track_adapter.cc
index 8360360..f8130733 100644
--- a/content/renderer/media/stream/remote_media_stream_track_adapter.cc
+++ b/content/renderer/media/stream/remote_media_stream_track_adapter.cc
@@ -47,9 +47,8 @@
   capabilities.device_id = blink::WebString::FromUTF8(id());
   web_track()->Source().SetCapabilities(capabilities);
 
-  MediaStreamVideoTrack* media_stream_track = new MediaStreamVideoTrack(
-      video_source, MediaStreamVideoSource::ConstraintsCallback(), enabled);
-  web_track()->SetTrackData(media_stream_track);
+  web_track()->SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
+      video_source, MediaStreamVideoSource::ConstraintsCallback(), enabled));
 }
 
 RemoteAudioTrackAdapter::RemoteAudioTrackAdapter(
diff --git a/content/renderer/media/stream/user_media_client_impl.cc b/content/renderer/media/stream/user_media_client_impl.cc
index a211cf3..dd6f5d0f 100644
--- a/content/renderer/media/stream/user_media_client_impl.cc
+++ b/content/renderer/media/stream/user_media_client_impl.cc
@@ -222,8 +222,9 @@
                        base::Unretained(this)));
   } else {
     DCHECK(current_request.IsStopTrack());
-    MediaStreamTrack* track =
-        MediaStreamTrack::GetTrack(current_request.web_track_to_stop());
+    blink::PlatformMediaStreamTrack* track =
+        blink::PlatformMediaStreamTrack::GetTrack(
+            current_request.web_track_to_stop());
     if (track) {
       track->StopAndNotify(
           base::BindOnce(&UserMediaClientImpl::CurrentRequestCompleted,
diff --git a/content/renderer/media/stream/user_media_client_impl_unittest.cc b/content/renderer/media/stream/user_media_client_impl_unittest.cc
index 44d012c..9d9faf46 100644
--- a/content/renderer/media/stream/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/stream/user_media_client_impl_unittest.cc
@@ -21,7 +21,6 @@
 #include "content/renderer/media/stream/media_stream_constraints_util.h"
 #include "content/renderer/media/stream/media_stream_constraints_util_video_content.h"
 #include "content/renderer/media/stream/media_stream_device_observer.h"
-#include "content/renderer/media/stream/media_stream_track.h"
 #include "content/renderer/media/stream/mock_constraint_factory.h"
 #include "content/renderer/media/stream/mock_media_stream_video_source.h"
 #include "content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h"
@@ -30,6 +29,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/mediastream/media_devices.h"
+#include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_stream.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
@@ -703,14 +703,16 @@
 
   blink::WebVector<blink::WebMediaStreamTrack> audio_tracks =
       mixed_desc.AudioTracks();
-  MediaStreamTrack* audio_track = MediaStreamTrack::GetTrack(audio_tracks[0]);
+  blink::PlatformMediaStreamTrack* audio_track =
+      blink::PlatformMediaStreamTrack::GetTrack(audio_tracks[0]);
   audio_track->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_dispatcher_host_.stop_audio_device_counter());
 
   blink::WebVector<blink::WebMediaStreamTrack> video_tracks =
       mixed_desc.VideoTracks();
-  MediaStreamTrack* video_track = MediaStreamTrack::GetTrack(video_tracks[0]);
+  blink::PlatformMediaStreamTrack* video_track =
+      blink::PlatformMediaStreamTrack::GetTrack(video_tracks[0]);
   video_track->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_dispatcher_host_.stop_video_device_counter());
@@ -727,28 +729,32 @@
 
   blink::WebVector<blink::WebMediaStreamTrack> audio_tracks1 =
       desc1.AudioTracks();
-  MediaStreamTrack* audio_track1 = MediaStreamTrack::GetTrack(audio_tracks1[0]);
+  blink::PlatformMediaStreamTrack* audio_track1 =
+      blink::PlatformMediaStreamTrack::GetTrack(audio_tracks1[0]);
   audio_track1->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, mock_dispatcher_host_.stop_audio_device_counter());
 
   blink::WebVector<blink::WebMediaStreamTrack> audio_tracks2 =
       desc2.AudioTracks();
-  MediaStreamTrack* audio_track2 = MediaStreamTrack::GetTrack(audio_tracks2[0]);
+  blink::PlatformMediaStreamTrack* audio_track2 =
+      blink::PlatformMediaStreamTrack::GetTrack(audio_tracks2[0]);
   audio_track2->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_dispatcher_host_.stop_audio_device_counter());
 
   blink::WebVector<blink::WebMediaStreamTrack> video_tracks1 =
       desc1.VideoTracks();
-  MediaStreamTrack* video_track1 = MediaStreamTrack::GetTrack(video_tracks1[0]);
+  blink::PlatformMediaStreamTrack* video_track1 =
+      blink::PlatformMediaStreamTrack::GetTrack(video_tracks1[0]);
   video_track1->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, mock_dispatcher_host_.stop_video_device_counter());
 
   blink::WebVector<blink::WebMediaStreamTrack> video_tracks2 =
       desc2.VideoTracks();
-  MediaStreamTrack* video_track2 = MediaStreamTrack::GetTrack(video_tracks2[0]);
+  blink::PlatformMediaStreamTrack* video_track2 =
+      blink::PlatformMediaStreamTrack::GetTrack(video_tracks2[0]);
   video_track2->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_dispatcher_host_.stop_video_device_counter());
@@ -854,14 +860,16 @@
 
   blink::WebVector<blink::WebMediaStreamTrack> audio_tracks =
       mixed_desc.AudioTracks();
-  MediaStreamTrack* audio_track = MediaStreamTrack::GetTrack(audio_tracks[0]);
+  blink::PlatformMediaStreamTrack* audio_track =
+      blink::PlatformMediaStreamTrack::GetTrack(audio_tracks[0]);
   audio_track->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_dispatcher_host_.stop_audio_device_counter());
 
   blink::WebVector<blink::WebMediaStreamTrack> video_tracks =
       mixed_desc.VideoTracks();
-  MediaStreamTrack* video_track = MediaStreamTrack::GetTrack(video_tracks[0]);
+  blink::PlatformMediaStreamTrack* video_track =
+      blink::PlatformMediaStreamTrack::GetTrack(video_tracks[0]);
   video_track->Stop();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_dispatcher_host_.stop_video_device_counter());
@@ -1250,7 +1258,7 @@
   // Try to use applyConstraints() to change the first track to 800x600@30Hz.
   // after stopping the second track. In this case, the source is left with a
   // single track and it supports reconfiguration to the requested mode.
-  MediaStreamTrack::GetTrack(web_track2)->Stop();
+  blink::PlatformMediaStreamTrack::GetTrack(web_track2)->Stop();
   ApplyConstraintsVideoMode(web_track, 800, 600, 30.0);
   CheckVideoSourceAndTrack(source, 800, 600, 30.0, web_track, 800, 600, 30.0);
 }
@@ -1311,7 +1319,8 @@
   CheckVideoSourceAndTrack(source, 1024, 768, 20.0, web_track, 1024, 768, 20.0);
 
   // Try to switch the source and track to 640x480 after stopping the track.
-  MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
+  blink::PlatformMediaStreamTrack* track =
+      blink::PlatformMediaStreamTrack::GetTrack(web_track);
   track->Stop();
   EXPECT_EQ(web_track.Source().GetReadyState(),
             blink::WebMediaStreamSource::kReadyStateEnded);
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index 390b0ea..5b78cc0 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -1115,13 +1115,15 @@
     GetUserMediaRequestFailed(result, constraint_name);
 
     for (auto& web_track : request_info->web_stream()->AudioTracks()) {
-      MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
+      blink::PlatformMediaStreamTrack* track =
+          blink::PlatformMediaStreamTrack::GetTrack(web_track);
       if (track)
         track->Stop();
     }
 
     for (auto& web_track : request_info->web_stream()->VideoTracks()) {
-      MediaStreamTrack* track = MediaStreamTrack::GetTrack(web_track);
+      blink::PlatformMediaStreamTrack* track =
+          blink::PlatformMediaStreamTrack::GetTrack(web_track);
       if (track)
         track->Stop();
     }
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 6e5146c5..36c9ff1 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -28,7 +28,6 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/renderer/media/stream/media_stream_constraints_util.h"
-#include "content/renderer/media/stream/media_stream_track.h"
 #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/peer_connection_tracker.h"
 #include "content/renderer/media/webrtc/rtc_data_channel_handler.h"
@@ -41,6 +40,7 @@
 #include "content/renderer/media/webrtc/webrtc_uma_histograms.h"
 #include "content/renderer/render_thread_impl.h"
 #include "media/base/media_switches.h"
+#include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_rtc_answer_options.h"
 #include "third_party/blink/public/platform/web_rtc_data_channel_init.h"
@@ -846,15 +846,13 @@
   }
 
   void OnIceConnectionChange(
-      PeerConnectionInterface::IceConnectionState new_state) override {}
-  void OnStandardizedIceConnectionChange(
-
       PeerConnectionInterface::IceConnectionState new_state) override {
     if (!main_thread_->BelongsToCurrentThread()) {
       main_thread_->PostTask(
-          FROM_HERE, base::BindOnce(&RTCPeerConnectionHandler::Observer::
-                                        OnStandardizedIceConnectionChange,
-                                    this, new_state));
+          FROM_HERE,
+          base::BindOnce(
+              &RTCPeerConnectionHandler::Observer::OnIceConnectionChange, this,
+              new_state));
     } else if (handler_) {
       handler_->OnIceConnectionChange(new_state);
     }
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index f8bfea7..c137a33 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -209,8 +209,6 @@
       webrtc::PeerConnectionInterface::SignalingState new_state);
   void OnIceConnectionChange(
       webrtc::PeerConnectionInterface::IceConnectionState new_state);
-  void OnStandardizedIceConnectionChange(
-      webrtc::PeerConnectionInterface::IceConnectionState new_state);
   void OnConnectionChange(
       webrtc::PeerConnectionInterface::PeerConnectionState new_state);
   void OnIceGatheringChange(
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index df38ed7..a8c7f21 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -1047,7 +1047,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionNew));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionChecking;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1057,7 +1057,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionChecking));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionConnected;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1067,7 +1067,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionConnected));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionCompleted;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1077,7 +1077,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionCompleted));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionFailed;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1087,7 +1087,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionFailed));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionDisconnected;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1097,7 +1097,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionDisconnected));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionClosed;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1107,7 +1107,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionClosed));
-  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
+  pc_handler_->observer()->OnIceConnectionChange(new_state);
 }
 
 TEST_F(RTCPeerConnectionHandlerTest, OnIceGatheringChange) {
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
index 1bfd71b..7ce6bff 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
@@ -144,7 +144,7 @@
     const blink::WebMediaStreamTrack& web_track) {
   DCHECK(main_thread_->BelongsToCurrentThread());
   EnsureTrackIsInitialized();
-  return web_track_.GetTrackData() == web_track.GetTrackData();
+  return web_track_.GetPlatformTrack() == web_track.GetPlatformTrack();
 }
 
 void WebRtcMediaStreamTrackAdapter::InitializeLocalAudioTrack(
diff --git a/content/renderer/media/webrtc_local_audio_source_provider_unittest.cc b/content/renderer/media/webrtc_local_audio_source_provider_unittest.cc
index bb0b138..ed9094bc 100644
--- a/content/renderer/media/webrtc_local_audio_source_provider_unittest.cc
+++ b/content/renderer/media/webrtc_local_audio_source_provider_unittest.cc
@@ -33,7 +33,8 @@
                             false /* remote */);
     blink_track_.Initialize(blink::WebString::FromUTF8("audio_track"),
                             audio_source);
-    blink_track_.SetTrackData(new MediaStreamAudioTrack(true));
+    blink_track_.SetPlatformTrack(
+        std::make_unique<MediaStreamAudioTrack>(true));
     source_provider_.reset(new WebRtcLocalAudioSourceProvider(blink_track_));
     source_provider_->SetSinkParamsForTesting(sink_params_);
     source_provider_->OnSetFormat(source_params_);
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler.cc b/content/renderer/media_capture_from_element/canvas_capture_handler.cc
index b9a3a21..d7d43815 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler.cc
@@ -485,7 +485,7 @@
       false /* is_device_capture */));
 
   web_track->Initialize(webkit_source);
-  web_track->SetTrackData(new MediaStreamVideoTrack(
+  web_track->SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
       media_stream_source, MediaStreamVideoSource::ConstraintsCallback(),
       true));
 }
diff --git a/content/renderer/media_recorder/media_recorder_handler.cc b/content/renderer/media_recorder/media_recorder_handler.cc
index ef9fb60..a8a57115 100644
--- a/content/renderer/media_recorder/media_recorder_handler.cc
+++ b/content/renderer/media_recorder/media_recorder_handler.cc
@@ -14,7 +14,6 @@
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
 #include "content/renderer/media/stream/media_stream_audio_track.h"
-#include "content/renderer/media/stream/media_stream_track.h"
 #include "content/renderer/media/webrtc/webrtc_uma_histograms.h"
 #include "content/renderer/media_recorder/audio_track_recorder.h"
 #include "media/base/audio_bus.h"
@@ -26,6 +25,7 @@
 #include "media/base/video_frame.h"
 #include "media/muxers/webm_muxer.h"
 #include "third_party/blink/public/platform/modules/media_capabilities/web_media_configuration.h"
+#include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
 #include "third_party/blink/public/platform/scoped_web_callbacks.h"
 #include "third_party/blink/public/platform/web_media_recorder_handler_client.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index bb709679..3120f40 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -402,7 +402,7 @@
       weak_ptr_factory_(this) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   DCHECK(!track_.IsNull());
-  DCHECK(track_.GetTrackData());
+  DCHECK(track_.GetPlatformTrack());
 
   initialize_encoder_callback_ = base::Bind(
       &VideoTrackRecorder::InitializeEncoder, weak_ptr_factory_.GetWeakPtr(),
diff --git a/content/renderer/media_recorder/video_track_recorder_unittest.cc b/content/renderer/media_recorder/video_track_recorder_unittest.cc
index e95b10c..3d0993b2 100644
--- a/content/renderer/media_recorder/video_track_recorder_unittest.cc
+++ b/content/renderer/media_recorder/video_track_recorder_unittest.cc
@@ -79,7 +79,7 @@
     track_ = new MediaStreamVideoTrack(
         mock_source_, blink::PlatformMediaStreamSource::ConstraintsCallback(),
         true /* enabled */);
-    blink_track_.SetTrackData(track_);
+    blink_track_.SetPlatformTrack(base::WrapUnique(track_));
 
     // Paranoia checks.
     EXPECT_EQ(blink_track_.Source().GetPlatformSource(),
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 34b59df..c1c39e26 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3884,13 +3884,13 @@
 
   // Synchronously notify the browser of a child frame creation to get the
   // routing_id for the RenderFrame.
-  int child_routing_id = MSG_ROUTING_NONE;
-  base::UnguessableToken devtools_frame_token;
   FrameHostMsg_CreateChildFrame_Params params;
-  FrameHostMsg_CreateChildFrame_Params_Reply params_reply;
   params.parent_routing_id = routing_id_;
   params.scope = scope;
   params.frame_name = name.Utf8();
+
+  FrameHostMsg_CreateChildFrame_Params_Reply params_reply;
+
   // The unique name generation logic was moved out of Blink, so for historical
   // reasons, unique name generation needs to take something called the
   // |fallback_name| into account. Normally, unique names are generated based on
@@ -3915,15 +3915,13 @@
       ConvertWebFrameOwnerPropertiesToFrameOwnerProperties(
           frame_owner_properties);
   params.frame_owner_element_type = frame_owner_element_type;
-  Send(new FrameHostMsg_CreateChildFrame(params, &child_routing_id,
-                                         &params_reply, &devtools_frame_token));
-
-  // Allocation of routing id failed, so we can't create a child frame. This can
-  // happen if the synchronous IPC message above has failed.  This can
-  // legitimately happen when the browser process has already destroyed
-  // RenderProcessHost, but the renderer process hasn't quit yet.
-  if (child_routing_id == MSG_ROUTING_NONE)
+  if (!Send(new FrameHostMsg_CreateChildFrame(params, &params_reply))) {
+    // Allocation of routing id failed, so we can't create a child frame. This can
+    // happen if the synchronous IPC message above has failed.  This can
+    // legitimately happen when the browser process has already destroyed
+    // RenderProcessHost, but the renderer process hasn't quit yet.
     return nullptr;
+  }
 
   DCHECK(params_reply.new_interface_provider.is_valid());
   service_manager::mojom::InterfaceProviderPtr child_interface_provider;
@@ -3956,14 +3954,15 @@
 
   // Tracing analysis uses this to find main frames when this value is
   // MSG_ROUTING_NONE, and build the frame tree otherwise.
-  TRACE_EVENT2("navigation,rail", "RenderFrameImpl::createChildFrame",
-               "id", routing_id_,
-               "child", child_routing_id);
+  TRACE_EVENT2("navigation,rail", "RenderFrameImpl::createChildFrame", "id",
+               routing_id_, "child", params_reply.child_routing_id);
 
   // Create the RenderFrame and WebLocalFrame, linking the two.
-  RenderFrameImpl* child_render_frame = RenderFrameImpl::Create(
-      render_view_, child_routing_id, std::move(child_interface_provider),
-      std::move(document_interface_broker_content), devtools_frame_token);
+  RenderFrameImpl* child_render_frame =
+      RenderFrameImpl::Create(render_view_, params_reply.child_routing_id,
+                              std::move(child_interface_provider),
+                              std::move(document_interface_broker_content),
+                              params_reply.devtools_frame_token);
   child_render_frame->unique_name_helper_.set_propagated_name(
       params.frame_unique_name);
   if (params.is_created_by_script)
@@ -4278,7 +4277,6 @@
 void RenderFrameImpl::DidCommitProvisionalLoad(
     const blink::WebHistoryItem& item,
     blink::WebHistoryCommitType commit_type,
-    blink::WebGlobalObjectReusePolicy global_object_reuse_policy,
     mojo::ScopedMessagePipeHandle document_interface_broker_blink_handle) {
   TRACE_EVENT2("navigation,rail", "RenderFrameImpl::didCommitProvisionalLoad",
                "id", routing_id_,
@@ -4367,13 +4365,9 @@
   blink::mojom::DocumentInterfaceBrokerRequest
       document_interface_broker_request;
 
-  // TODO(crbug.com/718652): check whether
-  // |document_interface_broker_blink_handle| is valid instead and remove the
-  // |global_object_reuse_policy| parameter.
-  bool reuse_existing_interfaces =
-      (global_object_reuse_policy ==
-       blink::WebGlobalObjectReusePolicy::kUseExisting);
-  if (!reuse_existing_interfaces) {
+  // blink passes a valid DocumentInterfaceBroker handle when the new pipe needs
+  // to be bound.
+  if (document_interface_broker_blink_handle.is_valid()) {
     // If we're navigating to a new document, bind |remote_interfaces_| to a new
     // message pipe. The request end of the new InterfaceProvider interface will
     // be sent over as part of DidCommitProvisionalLoad. After the RFHI receives
@@ -4439,13 +4433,13 @@
 
   DidCommitNavigationInternal(
       item, commit_type, false /* was_within_same_document */, transition,
-      reuse_existing_interfaces
-          ? nullptr
-          : mojom::DidCommitProvisionalLoadInterfaceParams::New(
+      document_interface_broker_blink_handle.is_valid()
+          ? mojom::DidCommitProvisionalLoadInterfaceParams::New(
                 std::move(remote_interface_provider_request),
                 std::move(document_interface_broker_request),
                 blink::mojom::DocumentInterfaceBrokerRequest(
-                    std::move(document_interface_broker_blink_handle))));
+                    std::move(document_interface_broker_blink_handle)))
+          : nullptr);
 
   // Record time between receiving the message to commit the navigation until it
   // has committed. Only successful cross-document navigation handled by the
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index e3a09c0..c813029 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -684,7 +684,6 @@
   void DidCommitProvisionalLoad(
       const blink::WebHistoryItem& item,
       blink::WebHistoryCommitType commit_type,
-      blink::WebGlobalObjectReusePolicy global_object_reuse_policy,
       mojo::ScopedMessagePipeHandle document_interface_broker_blink_handle)
       override;
   void DidCreateNewDocument() override;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 32c82e62..0556233 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -312,7 +312,6 @@
   blink::mojom::DocumentInterfaceBrokerPtr stub_document_interface_broker;
   frame()->DidCommitProvisionalLoad(
       item, blink::kWebStandardCommit,
-      blink::WebGlobalObjectReusePolicy::kCreateNew,
       mojo::MakeRequest(&stub_document_interface_broker).PassMessagePipe());
   EXPECT_EQ(SERVER_LOFI_ON, frame()->GetPreviewsState());
 
@@ -325,7 +324,6 @@
   // but serves the purpose of testing the LoFi state logic.
   GetMainRenderFrame()->DidCommitProvisionalLoad(
       item, blink::kWebStandardCommit,
-      blink::WebGlobalObjectReusePolicy::kCreateNew,
       mojo::MakeRequest(&stub_document_interface_broker).PassMessagePipe());
   EXPECT_EQ(PREVIEWS_UNSPECIFIED, GetMainRenderFrame()->GetPreviewsState());
   // The subframe would be deleted here after a cross-document navigation. It
@@ -375,7 +373,6 @@
     blink::mojom::DocumentInterfaceBrokerPtr stub_document_interface_broker;
     frame()->DidCommitProvisionalLoad(
         item, blink::kWebStandardCommit,
-        blink::WebGlobalObjectReusePolicy::kCreateNew,
         mojo::MakeRequest(&stub_document_interface_broker).PassMessagePipe());
     EXPECT_EQ(tests[i].type, frame()->GetEffectiveConnectionType());
 
@@ -386,7 +383,6 @@
 
     GetMainRenderFrame()->DidCommitProvisionalLoad(
         item, blink::kWebStandardCommit,
-        blink::WebGlobalObjectReusePolicy::kCreateNew,
         mojo::MakeRequest(&stub_document_interface_broker).PassMessagePipe());
     EXPECT_EQ(blink::WebEffectiveConnectionType::kTypeUnknown,
               GetMainRenderFrame()->GetEffectiveConnectionType());
diff --git a/content/test/data/media/peerconnection-call-data.html b/content/test/data/media/peerconnection-call-data.html
index baca0c1..67f7e5c 100644
--- a/content/test/data/media/peerconnection-call-data.html
+++ b/content/test/data/media/peerconnection-call-data.html
@@ -240,7 +240,7 @@
   }
 
   function createConnection(constraints, remoteView) {
-    var pc = new RTCPeerConnection(null, constraints);
+    var pc = new RTCPeerConnection({sdpSemantics:'plan-b'}, constraints);
     pc.onaddstream = function(event) {
       onRemoteStream(event, remoteView);
     }
diff --git a/content/test/data/media/peerconnection-call.html b/content/test/data/media/peerconnection-call.html
index 8b21bae..82c4255 100644
--- a/content/test/data/media/peerconnection-call.html
+++ b/content/test/data/media/peerconnection-call.html
@@ -508,7 +508,7 @@
   }
 
   function createConnection(constraints, remoteView) {
-    var pc = new RTCPeerConnection(null, constraints);
+    var pc = new RTCPeerConnection({sdpSemantics:'plan-b'}, constraints);
     pc.onaddstream = function(event) {
       onRemoteStream(event, remoteView);
     }
diff --git a/device/BUILD.gn b/device/BUILD.gn
index ccc1d517..f6c7e0d 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -20,6 +20,7 @@
 test("device_unittests") {
   sources = [
     "base/synchronization/one_writer_seqlock_unittest.cc",
+    "bluetooth/bluetooth_adapter_mac_metrics_unittest.mm",
     "bluetooth/bluetooth_adapter_mac_unittest.mm",
     "bluetooth/bluetooth_adapter_unittest.cc",
     "bluetooth/bluetooth_adapter_win_unittest.cc",
diff --git a/device/bluetooth/bluetooth_adapter_mac_metrics.mm b/device/bluetooth/bluetooth_adapter_mac_metrics.mm
index c6ff76f8..ae68c5f5 100644
--- a/device/bluetooth/bluetooth_adapter_mac_metrics.mm
+++ b/device/bluetooth/bluetooth_adapter_mac_metrics.mm
@@ -8,7 +8,7 @@
 #import <Foundation/Foundation.h>
 
 #include "base/mac/mac_util.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 
 namespace {
 
@@ -128,89 +128,70 @@
 void RecordDidFailToConnectPeripheralResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
+  base::UmaHistogramSparse(
       "Bluetooth.MacOS.Errors.DidFailToConnectToPeripheral",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+      static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidDisconnectPeripheralResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidDisconnectPeripheral",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidDisconnectPeripheral",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidDiscoverPrimaryServicesResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidDiscoverPrimaryServices",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidDiscoverPrimaryServices",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidDiscoverCharacteristicsResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidDiscoverCharacteristics",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidDiscoverCharacteristics",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidUpdateValueResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidUpdateValue",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidUpdateValue",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidWriteValueResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidWriteValue",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidWriteValue",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidUpdateNotificationStateResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidUpdateNotificationState",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidUpdateNotificationState",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidDiscoverDescriptorsResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidDiscoverDescriptors",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidDiscoverDescriptors",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidUpdateValueForDescriptorResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidUpdateValueForDescriptor",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidUpdateValueForDescriptor",
+                           static_cast<int>(histogram_macos_error));
 }
 
 void RecordDidWriteValueForDescriptorResult(NSError* error) {
   MacOSBluetoothOperationsResult histogram_macos_error =
       GetMacOSOperationResultFromNSError(error);
-  UMA_HISTOGRAM_ENUMERATION(
-      "Bluetooth.MacOS.Errors.DidWriteValueForDescriptor",
-      static_cast<int>(histogram_macos_error),
-      static_cast<int>(MacOSBluetoothOperationsResult::MAX));
+  base::UmaHistogramSparse("Bluetooth.MacOS.Errors.DidWriteValueForDescriptor",
+                           static_cast<int>(histogram_macos_error));
 }
diff --git a/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm
new file mode 100644
index 0000000..00d17c1
--- /dev/null
+++ b/device/bluetooth/bluetooth_adapter_mac_metrics_unittest.mm
@@ -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.
+
+#include "base/test/metrics/histogram_tester.h"
+#include "device/bluetooth/test/bluetooth_test_mac.h"
+
+namespace device {
+
+class BluetoothAdapterMacMetricsTest : public BluetoothTest {
+ public:
+  void FakeDeviceBoilerPlate() {
+    if (!PlatformSupportsLowEnergy()) {
+      LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
+      return;
+    }
+    InitWithFakeAdapter();
+    StartLowEnergyDiscoverySession();
+    device_ = SimulateLowEnergyDevice(3);
+  }
+
+  void FakeServiceBoilerPlate() {
+    ASSERT_NO_FATAL_FAILURE(FakeDeviceBoilerPlate());
+
+    device_->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
+                                  GetConnectErrorCallback(Call::NOT_EXPECTED));
+    SimulateGattConnection(device_);
+    base::RunLoop().RunUntilIdle();
+    SimulateGattServicesDiscovered(
+        device_, std::vector<std::string>({kTestUUIDGenericAccess}));
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(1u, device_->GetGattServices().size());
+    service_ = device_->GetGattServices()[0];
+  }
+
+  void FakeCharacteristicBoilerplate(int property = 0) {
+    ASSERT_NO_FATAL_FAILURE(FakeServiceBoilerPlate());
+
+    SimulateGattCharacteristic(service_, kTestUUIDDeviceName, property);
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(1u, service_->GetCharacteristics().size());
+    characteristic_ = service_->GetCharacteristics()[0];
+  }
+
+  BluetoothDevice* device_ = nullptr;
+  BluetoothRemoteGattService* service_ = nullptr;
+  BluetoothRemoteGattCharacteristic* characteristic_ = nullptr;
+};
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidFailToConnectToPeripheralError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeDeviceBoilerPlate());
+  SimulateGattConnectionError(device_, BluetoothDevice::ERROR_FAILED);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidFailToConnectToPeripheral", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidDisconnectPeripheral) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeDeviceBoilerPlate());
+  SimulateGattDisconnectionError(device_);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidDisconnectPeripheral", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidDiscoverPrimaryServicesError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeDeviceBoilerPlate());
+  SimulateGattConnection(device_);
+  SimulateDidDiscoverServicesMacWithError(device_);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidDiscoverPrimaryServices", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidDiscoverCharacteristicsError) {
+  ASSERT_NO_FATAL_FAILURE(FakeServiceBoilerPlate());
+  base::HistogramTester histogram_tester;
+  SimulateDidDiscoverCharacteristicsWithErrorMac(service_);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidDiscoverCharacteristics", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidUpdateValueError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+      BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
+
+  SimulateGattCharacteristicReadError(
+      characteristic_, BluetoothRemoteGattService::GATT_ERROR_FAILED);
+  histogram_tester.ExpectTotalCount("Bluetooth.MacOS.Errors.DidUpdateValue", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidWriteValueError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+      BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
+
+  SimulateGattCharacteristicWriteError(
+      characteristic_, BluetoothRemoteGattService::GATT_ERROR_FAILED);
+  histogram_tester.ExpectTotalCount("Bluetooth.MacOS.Errors.DidWriteValue", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidUpdateNotificationStateError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+      BluetoothRemoteGattCharacteristic::PROPERTY_NOTIFY));
+  SimulateGattDescriptor(
+      characteristic_,
+      BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
+          .canonical_value());
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(1u, characteristic_->GetDescriptors().size());
+  characteristic_->StartNotifySession(GetNotifyCallback(Call::NOT_EXPECTED),
+                                      GetGattErrorCallback(Call::EXPECTED));
+  ExpectedChangeNotifyValueAttempts(1);
+  ExpectedNotifyValue(NotifyValueState::NOTIFY);
+  SimulateGattNotifySessionStartError(
+      characteristic_, BluetoothRemoteGattService::GATT_ERROR_FAILED);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidUpdateNotificationState", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidDiscoverDescriptorsError) {
+  ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+      BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
+  base::HistogramTester histogram_tester;
+  SimulateDidDiscoverDescriptorsWithErrorMac(characteristic_);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidDiscoverDescriptors", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidUpdateValueForDescriptorError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+      BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
+  SimulateGattDescriptor(
+      characteristic_,
+      BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
+          .canonical_value());
+  EXPECT_EQ(1u, characteristic_->GetDescriptors().size());
+  BluetoothRemoteGattDescriptor* descriptor =
+      characteristic_->GetDescriptors()[0];
+  descriptor->ReadRemoteDescriptor(GetReadValueCallback(Call::NOT_EXPECTED),
+                                   GetGattErrorCallback(Call::EXPECTED));
+  SimulateGattDescriptorUpdateError(
+      descriptor, BluetoothRemoteGattService::GATT_ERROR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidUpdateValueForDescriptor", 1);
+}
+
+TEST_F(BluetoothAdapterMacMetricsTest, DidWriteValueForDescriptorError) {
+  base::HistogramTester histogram_tester;
+  ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate(
+      BluetoothRemoteGattCharacteristic::PROPERTY_WRITE));
+  SimulateGattDescriptor(
+      characteristic_,
+      BluetoothRemoteGattDescriptor::ClientCharacteristicConfigurationUuid()
+          .canonical_value());
+  EXPECT_EQ(1u, characteristic_->GetDescriptors().size());
+  BluetoothRemoteGattDescriptor* descriptor =
+      characteristic_->GetDescriptors()[0];
+  std::vector<uint8_t> empty_vector;
+  descriptor->WriteRemoteDescriptor(empty_vector,
+                                    GetCallback(Call::NOT_EXPECTED),
+                                    GetGattErrorCallback(Call::EXPECTED));
+  SimulateGattDescriptorWriteError(
+      descriptor, BluetoothRemoteGattService::GATT_ERROR_FAILED);
+  histogram_tester.ExpectTotalCount(
+      "Bluetooth.MacOS.Errors.DidWriteValueForDescriptor", 1);
+}
+
+}
\ No newline at end of file
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h
index 91cb81e..d9ad3e2 100644
--- a/device/bluetooth/test/bluetooth_test.h
+++ b/device/bluetooth/test/bluetooth_test.h
@@ -320,6 +320,9 @@
   // Simulates GattConnection disconnecting.
   virtual void SimulateGattDisconnection(BluetoothDevice* device) {}
 
+  // Simulates Error in GattConnection disconnecting.
+  virtual void SimulateGattDisconnectionError(BluetoothDevice* device) {}
+
   // Simulates an event where the OS breaks the Gatt connection. Defaults to
   // SimulateGattDisconnection(device).
   virtual void SimulateDeviceBreaksConnection(BluetoothDevice* device);
@@ -545,6 +548,11 @@
       BluetoothRemoteGattDescriptor* descriptor,
       BluetoothRemoteGattService::GattErrorCode) {}
 
+  // Simulates a Descriptor Update operation failing with a GattErrorCode.
+  virtual void SimulateGattDescriptorUpdateError(
+      BluetoothRemoteGattDescriptor* descriptor,
+      BluetoothRemoteGattService::GattErrorCode) {}
+
   // Simulates a Descriptor Write operation failing synchronously once for
   // an unknown reason.
   virtual void SimulateGattDescriptorWriteWillFailSynchronouslyOnce(
diff --git a/device/bluetooth/test/bluetooth_test_mac.h b/device/bluetooth/test/bluetooth_test_mac.h
index 6ea1428..773878ca 100644
--- a/device/bluetooth/test/bluetooth_test_mac.h
+++ b/device/bluetooth/test/bluetooth_test_mac.h
@@ -51,6 +51,7 @@
       BluetoothDevice* device,
       BluetoothDevice::ConnectErrorCode errorCode) override;
   void SimulateGattDisconnection(BluetoothDevice* device) override;
+  void SimulateGattDisconnectionError(BluetoothDevice* device) override;
   void SimulateGattServicesDiscovered(
       BluetoothDevice* device,
       const std::vector<std::string>& uuids) override;
@@ -98,6 +99,9 @@
   void SimulateGattDescriptorWriteError(
       BluetoothRemoteGattDescriptor* descriptor,
       BluetoothRemoteGattService::GattErrorCode error_code) override;
+  void SimulateGattDescriptorUpdateError(
+      BluetoothRemoteGattDescriptor* descriptor,
+      BluetoothRemoteGattService::GattErrorCode error_code) override;
   void ExpectedChangeNotifyValueAttempts(int attempts) override;
   void ExpectedNotifyValue(NotifyValueState expected_value_state) override;
 
@@ -106,12 +110,20 @@
   // set of attributes.
   // Simulates service discovery for a device.
   void SimulateDidDiscoverServicesMac(BluetoothDevice* device);
+  // Simulates error in service discovery for a device.
+  void SimulateDidDiscoverServicesMacWithError(BluetoothDevice* device);
   // Simulates characteristic discovery for a service.
   void SimulateDidDiscoverCharacteristicsMac(
       BluetoothRemoteGattService* service);
+  // Simulates error in characteristic discovery for a service.
+  void SimulateDidDiscoverCharacteristicsWithErrorMac(
+      BluetoothRemoteGattService* service);
   // Simulates descriptor discovery for a characteristic.
   void SimulateDidDiscoverDescriptorsMac(
       BluetoothRemoteGattCharacteristic* characteristic);
+  // Simulates error in descriptor discovery for a characteristic.
+  void SimulateDidDiscoverDescriptorsWithErrorMac(
+      BluetoothRemoteGattCharacteristic* characteristic);
   // CoreBluetooth can return NSData when reading remote gatt descriptors.
   // This methods simulate receiving NSData from CoreBluetooth.
   void SimulateGattDescriptorReadNSDataMac(
diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm
index a653574..5f218f2 100644
--- a/device/bluetooth/test/bluetooth_test_mac.mm
+++ b/device/bluetooth/test/bluetooth_test_mac.mm
@@ -95,6 +95,9 @@
 const char BluetoothTestMac::kTestPeripheralUUID2[] =
     "EC1B8F00-0000-0000-0000-000000000000";
 
+// Fake error domain for testing error metrics
+static NSString* const kDisconnectErrorDomain = @"FakeDisconnectErrorDomain";
+
 BluetoothTestMac::BluetoothTestMac() : BluetoothTestBase() {}
 
 BluetoothTestMac::~BluetoothTestMac() {}
@@ -327,6 +330,19 @@
                                      error:nil];
 }
 
+void BluetoothTestMac::SimulateGattDisconnectionError(BluetoothDevice* device) {
+  MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
+  [peripheral_mock setState:CBPeripheralStateDisconnected];
+  NSError* error = [NSError errorWithDomain:kDisconnectErrorDomain
+                                       code:CBErrorUnknown
+                                   userInfo:nil];
+  CBCentralManager* central_manager =
+      ObjCCast<CBCentralManager>(mock_central_manager_->get());
+  [central_manager.delegate centralManager:central_manager
+                   didDisconnectPeripheral:peripheral_mock.peripheral
+                                     error:error];
+}
+
 void BluetoothTestMac::SimulateGattServicesDiscovered(
     BluetoothDevice* device,
     const std::vector<std::string>& uuids) {
@@ -504,6 +520,13 @@
   [GetCBMockDescriptor(descriptor) simulateWriteWithError:error];
 }
 
+void BluetoothTestMac::SimulateGattDescriptorUpdateError(
+    BluetoothRemoteGattDescriptor* descriptor,
+    BluetoothRemoteGattService::GattErrorCode error_code) {
+  NSError* error = BluetoothDeviceMac::GetNSErrorFromGattErrorCode(error_code);
+  [GetCBMockDescriptor(descriptor) simulateUpdateWithError:error];
+}
+
 void BluetoothTestMac::ExpectedChangeNotifyValueAttempts(int attempts) {
   EXPECT_EQ(attempts, gatt_notify_characteristic_attempts_);
 }
@@ -525,6 +548,14 @@
   [GetMockCBPeripheral(device) mockDidDiscoverServices];
 }
 
+void BluetoothTestMac::SimulateDidDiscoverServicesMacWithError(
+    BluetoothDevice* device) {
+  NSError* error = [NSError errorWithDomain:CBErrorDomain
+                                       code:CBErrorUnknown
+                                   userInfo:nil];
+  [GetMockCBPeripheral(device) mockDidDiscoverServicesWithError:error];
+}
+
 void BluetoothTestMac::SimulateDidDiscoverCharacteristicsMac(
     BluetoothRemoteGattService* service) {
   BluetoothRemoteGattServiceMac* mac_gatt_service =
@@ -534,9 +565,21 @@
       mockDidDiscoverCharacteristicsForService:cb_service];
 }
 
+void BluetoothTestMac::SimulateDidDiscoverCharacteristicsWithErrorMac(
+    BluetoothRemoteGattService* service) {
+  auto* mac_gatt_service = static_cast<BluetoothRemoteGattServiceMac*>(service);
+  CBService* cb_service = mac_gatt_service->GetService();
+  NSError* error = [NSError errorWithDomain:CBErrorDomain
+                                       code:CBErrorUnknown
+                                   userInfo:nil];
+  [GetMockCBPeripheral(service)
+      mockDidDiscoverCharacteristicsForService:cb_service
+                                     WithError:error];
+}
+
 void BluetoothTestMac::SimulateDidDiscoverDescriptorsMac(
     BluetoothRemoteGattCharacteristic* characteristic) {
-  BluetoothRemoteGattCharacteristicMac* mac_gatt_characteristic =
+  auto* mac_gatt_characteristic =
       static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
   CBCharacteristic* cb_characteristic =
       mac_gatt_characteristic->GetCBCharacteristic();
@@ -544,6 +587,20 @@
       mockDidDiscoverDescriptorsForCharacteristic:cb_characteristic];
 }
 
+void BluetoothTestMac::SimulateDidDiscoverDescriptorsWithErrorMac(
+    BluetoothRemoteGattCharacteristic* characteristic) {
+  auto* mac_gatt_characteristic =
+      static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
+  CBCharacteristic* cb_characteristic =
+      mac_gatt_characteristic->GetCBCharacteristic();
+  NSError* error = [NSError errorWithDomain:CBErrorDomain
+                                       code:CBErrorUnknown
+                                   userInfo:nil];
+  [GetMockCBPeripheral(characteristic)
+      mockDidDiscoverDescriptorsForCharacteristic:cb_characteristic
+                                        WithError:error];
+}
+
 void BluetoothTestMac::SimulateGattDescriptorReadNSDataMac(
     BluetoothRemoteGattDescriptor* descriptor,
     const std::vector<uint8_t>& value) {
@@ -612,8 +669,7 @@
     BluetoothRemoteGattService* service,
     const std::string& characteristic_uuid,
     int properties) {
-  BluetoothRemoteGattServiceMac* mac_gatt_service =
-      static_cast<BluetoothRemoteGattServiceMac*>(service);
+  auto* mac_gatt_service = static_cast<BluetoothRemoteGattServiceMac*>(service);
   CBService* cb_service = mac_gatt_service->GetService();
   MockCBService* service_mock = ObjCCast<MockCBService>(cb_service);
   CBUUID* cb_uuid = [CBUUID UUIDWithString:@(characteristic_uuid.c_str())];
@@ -623,7 +679,7 @@
 void BluetoothTestMac::AddDescriptorToCharacteristicMac(
     BluetoothRemoteGattCharacteristic* characteristic,
     const std::string& uuid) {
-  BluetoothRemoteGattCharacteristicMac* mac_gatt_characteristic =
+  auto* mac_gatt_characteristic =
       static_cast<BluetoothRemoteGattCharacteristicMac*>(characteristic);
   CBCharacteristic* cb_characteristic =
       mac_gatt_characteristic->GetCBCharacteristic();
@@ -691,8 +747,7 @@
 
 MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
     BluetoothDevice* device) const {
-  BluetoothLowEnergyDeviceMac* device_mac =
-      static_cast<BluetoothLowEnergyDeviceMac*>(device);
+  auto* device_mac = static_cast<BluetoothLowEnergyDeviceMac*>(device);
   CBPeripheral* peripheral = device_mac->GetPeripheral();
   MockCBPeripheral* peripheral_mock = ObjCCast<MockCBPeripheral>(peripheral);
   DCHECK(peripheral_mock);
@@ -702,8 +757,7 @@
 MockCBPeripheral* BluetoothTestMac::GetMockCBPeripheral(
     BluetoothRemoteGattService* service) const {
   BluetoothDevice* device = service->GetDevice();
-  BluetoothLowEnergyDeviceMac* device_mac =
-      static_cast<BluetoothLowEnergyDeviceMac*>(device);
+  auto* device_mac = static_cast<BluetoothLowEnergyDeviceMac*>(device);
   CBPeripheral* cb_peripheral = device_mac->GetPeripheral();
   return ObjCCast<MockCBPeripheral>(cb_peripheral);
 }
diff --git a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h
index 0a2baed4..13a9851 100644
--- a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h
+++ b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h
@@ -21,6 +21,7 @@
 
 - (void)simulateReadWithValue:(id)value error:(NSError*)error;
 - (void)simulateWriteWithError:(NSError*)error;
+- (void)simulateUpdateWithError:(NSError*)error;
 
 @end
 
diff --git a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
index 1e0d82b..6c12d39 100644
--- a/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.mm
@@ -78,4 +78,11 @@
                             error:error];
 }
 
+- (void)simulateUpdateWithError:(NSError*)error {
+  CBPeripheral* peripheral = _characteristic.service.peripheral;
+  [peripheral.delegate peripheral:peripheral
+      didUpdateValueForDescriptor:self.descriptor
+                            error:error];
+}
+
 @end
diff --git a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h
index 04f1107..abd6ab7 100644
--- a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h
+++ b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h
@@ -39,12 +39,17 @@
 - (void)setState:(CBPeripheralState)state;
 - (void)removeAllServices;
 - (void)addServices:(NSArray*)services;
-- (void)didDiscoverServicesWithError:(NSError*)error;
+- (void)mockDidDiscoverServicesWithError:(NSError*)error;
 - (void)removeService:(CBService*)uuid;
 - (void)mockDidDiscoverServices;
+- (void)mockDidDiscoverCharacteristicsForService:(CBService*)service
+                                       WithError:(NSError*)error;
 - (void)mockDidDiscoverCharacteristicsForService:(CBService*)service;
 - (void)mockDidDiscoverDescriptorsForCharacteristic:
     (CBCharacteristic*)characteristic;
+- (void)mockDidDiscoverDescriptorsForCharacteristic:
+            (CBCharacteristic*)characteristic
+                                          WithError:(NSError*)error;
 - (void)mockDidDiscoverEvents;
 - (void)didModifyServices:(NSArray*)invalidatedServices;
 - (void)didDiscoverDescriptorsWithCharacteristic:
diff --git a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
index 9a2bbb8..283b010 100644
--- a/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
+++ b/device/bluetooth/test/mock_bluetooth_cbperipheral_mac.mm
@@ -139,7 +139,7 @@
   }
 }
 
-- (void)didDiscoverServicesWithError:(NSError*)error {
+- (void)mockDidDiscoverServicesWithError:(NSError*)error {
   [_delegate peripheral:self.peripheral didDiscoverServices:error];
 }
 
@@ -161,6 +161,13 @@
                                      error:nil];
 }
 
+- (void)mockDidDiscoverCharacteristicsForService:(CBService*)service
+                                       WithError:(NSError*)error {
+  [_delegate peripheral:self.peripheral
+      didDiscoverCharacteristicsForService:service
+                                     error:error];
+}
+
 - (void)mockDidDiscoverDescriptorsForCharacteristic:
     (CBCharacteristic*)characteristic {
   [_delegate peripheral:self.peripheral
@@ -168,6 +175,14 @@
                                         error:nil];
 }
 
+- (void)mockDidDiscoverDescriptorsForCharacteristic:
+            (CBCharacteristic*)characteristic
+                                          WithError:(NSError*)error {
+  [_delegate peripheral:self.peripheral
+      didDiscoverDescriptorsForCharacteristic:characteristic
+                                        error:error];
+}
+
 - (void)mockDidDiscoverEvents {
   [self mockDidDiscoverServices];
   // BluetoothLowEnergyDeviceMac is expected to call
diff --git a/docs/gpu/gpu_testing.md b/docs/gpu/gpu_testing.md
index c0715387..bd1b8821 100644
--- a/docs/gpu/gpu_testing.md
+++ b/docs/gpu/gpu_testing.md
@@ -169,7 +169,6 @@
 build. Many of the tests are simple executables:
 
 *   `angle_unittests`
-*   `content_gl_tests`
 *   `gl_tests`
 *   `gl_unittests`
 *   `tab_capture_end2end_tests`
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index b1826b9..99c7c77 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -92,7 +92,9 @@
     * [`//components/viz/.+`](https://cs.chromium.org/chromium/src/components/viz/)
     * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
     * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
     * [`//services/viz/.+`](https://cs.chromium.org/chromium/src/services/viz/)
     * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
     * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
@@ -119,11 +121,9 @@
 
   Path regular expressions:
     * [`//cc/.+`](https://cs.chromium.org/chromium/src/cc/)
-    * [`//third_party/blink/renderer/core/(svg|paint)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/renderer/core/(svg|paint)/)
-    * [`//third_party/blink/renderer/core/paint/compositing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/compositing/)
+    * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
+    * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
     * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
-    * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees)
-    * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees)
     * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint)
     * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/)
 
@@ -135,11 +135,9 @@
 * [linux_layout_tests_composite_after_paint](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux_layout_tests_composite_after_paint) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux_layout_tests_composite_after_paint)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux_layout_tests_composite_after_paint))
 
   Path regular expressions:
-    * [`//third_party/blink/renderer/core/paint/compositing/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/compositing/)
-    * [`//third_party/blink/renderer/core/(svg|paint)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/renderer/core/(svg|paint)/)
+    * [`//third_party/blink/renderer/core/paint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/paint/)
+    * [`//third_party/blink/renderer/core/svg/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/svg/)
     * [`//third_party/blink/renderer/platform/graphics/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/)
-    * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees)
-    * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees)
     * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint)
     * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+`](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/)
 
@@ -168,7 +166,9 @@
     * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
     * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
     * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
     * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
     * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
     * [`//ui/gl/.+`](https://cs.chromium.org/chromium/src/ui/gl/)
@@ -184,7 +184,9 @@
     * [`//chrome/browser/vr/.+`](https://cs.chromium.org/chromium/src/chrome/browser/vr/)
     * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
     * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
     * [`//services/shape_detection/.+`](https://cs.chromium.org/chromium/src/services/shape_detection/)
     * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
     * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
@@ -197,7 +199,9 @@
     * [`//content/test/gpu/.+`](https://cs.chromium.org/chromium/src/content/test/gpu/)
     * [`//device/vr/.+`](https://cs.chromium.org/chromium/src/device/vr/)
     * [`//gpu/.+`](https://cs.chromium.org/chromium/src/gpu/)
-    * [`//media/(audio|filters|gpu)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:media/(audio|filters|gpu)/)
+    * [`//media/audio/.+`](https://cs.chromium.org/chromium/src/media/audio/)
+    * [`//media/filters/.+`](https://cs.chromium.org/chromium/src/media/filters/)
+    * [`//media/gpu/.+`](https://cs.chromium.org/chromium/src/media/gpu/)
     * [`//testing/trigger_scripts/.+`](https://cs.chromium.org/chromium/src/testing/trigger_scripts/)
     * [`//third_party/blink/renderer/modules/vr/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/vr/)
     * [`//third_party/blink/renderer/modules/webgl/.+`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/webgl/)
@@ -208,12 +212,18 @@
 * [android_compile_x64_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x64_dbg) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_compile_x64_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x64_dbg))
 
   Path regular expressions:
-    * [`//sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/)
+    * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
+    * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
+    * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
+    * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
 
 * [android_compile_x86_dbg](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_compile_x86_dbg) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_compile_x86_dbg)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_compile_x86_dbg))
 
   Path regular expressions:
-    * [`//sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/.+`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/)
+    * [`//sandbox/linux/seccomp-bpf/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf/)
+    * [`//sandbox/linux/seccomp-bpf-helpers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/seccomp-bpf-helpers/)
+    * [`//sandbox/linux/system_headers/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/system_headers/)
+    * [`//sandbox/linux/tests/.+`](https://cs.chromium.org/chromium/src/sandbox/linux/tests/)
 
 * [android_cronet_tester](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android_cronet_tester) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+android_cronet_tester)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android_cronet_tester))
 
diff --git a/extensions/browser/url_loader_factory_manager_browsertest.cc b/extensions/browser/url_loader_factory_manager_browsertest.cc
index 1fc9ca58..4e716be9 100644
--- a/extensions/browser/url_loader_factory_manager_browsertest.cc
+++ b/extensions/browser/url_loader_factory_manager_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
@@ -307,8 +308,16 @@
   EXPECT_FALSE(DoContentScriptsMatch_Tab2_BarBlankFrame2());
 }
 
+// Flaky on MacOS since r622662. See https://crbug.com/921883
+#if defined(OS_MACOSX)
+#define MAYBE_ContentScriptMatching_NotAllFrames \
+  DISABLED_ContentScriptMatching_NotAllFrames
+#else
+#define MAYBE_ContentScriptMatching_NotAllFrames \
+  ContentScriptMatching_NotAllFrames
+#endif
 IN_PROC_BROWSER_TEST_F(URLLoaderFactoryManagerBrowserTest,
-                       ContentScriptMatching_NotAllFrames) {
+                       MAYBE_ContentScriptMatching_NotAllFrames) {
   SetUpFrameTree();
   ASSERT_FALSE(::testing::Test::HasFailure());
 
diff --git a/gpu/command_buffer/common/id_type.h b/gpu/command_buffer/common/id_type.h
index 0d009c61..e3efbfa 100644
--- a/gpu/command_buffer/common/id_type.h
+++ b/gpu/command_buffer/common/id_type.h
@@ -60,6 +60,7 @@
   bool operator==(const IdType& other) const { return value_ == other.value_; }
   bool operator!=(const IdType& other) const { return value_ != other.value_; }
   bool operator<(const IdType& other) const { return value_ < other.value_; }
+  bool operator<=(const IdType& other) const { return value_ <= other.value_; }
 
   // Hasher to use in std::unordered_map, std::unordered_set, etc.
   struct Hasher {
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 24d952d..9fc1a97 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -2,8 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/jumbo.gni")
 import("//build/config/chrome_build.gni")
+import("//build/config/jumbo.gni")
 import("//build/util/lastchange.gni")
 import("//build/util/process_version.gni")
 import("//headless/headless.gni")
@@ -110,16 +110,6 @@
   ]
 }
 
-service_manifest("headless_browser_manifest_overlay") {
-  source = "lib/browser/headless_browser_manifest_overlay.json"
-}
-
-service_manifest("headless_packaged_services_manifest_overlay") {
-  source = "lib/browser/headless_packaged_services_manifest_overlay.json"
-  packaged_services =
-      [ "//components/services/pdf_compositor:pdf_compositor_manifest" ]
-}
-
 grit("resources") {
   source = "lib/resources/headless_lib_resources.grd"
   outputs = [
@@ -127,14 +117,6 @@
     "$root_gen_dir/headless/headless_lib_resources.pak",
   ]
   source_is_generated = true
-  grit_flags = [
-    "-E",
-    "mojom_root=" + rebase_path(root_gen_dir, root_build_dir),
-  ]
-  deps = [
-    ":headless_browser_manifest_overlay",
-    ":headless_packaged_services_manifest_overlay",
-  ]
 }
 
 devtools_domains = [
@@ -421,6 +403,8 @@
     sources += [
       "lib/browser/headless_content_browser_client.cc",
       "lib/browser/headless_content_browser_client.h",
+      "lib/browser/headless_overlay_manifests.cc",
+      "lib/browser/headless_overlay_manifests.h",
       "lib/browser/headless_web_contents_impl.cc",
       "lib/browser/headless_web_contents_impl.h",
       "lib/headless_content_main_delegate.cc",
@@ -442,6 +426,8 @@
     deps += [
       "//components/crash/core/common:crash_key",
       "//components/security_state/content",
+      "//components/services/pdf_compositor:pdf_compositor_manifest",
+      "//components/services/pdf_compositor/public/interfaces",
       "//gin",
       "//third_party/blink/public:blink",
       "//third_party/blink/public:blink_headers",
@@ -573,6 +559,8 @@
     sources += [
       "lib/browser/headless_content_browser_client.cc",
       "lib/browser/headless_content_browser_client.h",
+      "lib/browser/headless_overlay_manifests.cc",
+      "lib/browser/headless_overlay_manifests.h",
       "lib/utility/headless_content_utility_client.cc",
       "lib/utility/headless_content_utility_client.h",
     ]
@@ -583,6 +571,8 @@
     "//base/test:run_all_unittests",
     "//base/test:test_support",
     "//components/security_state/content",
+    "//components/services/pdf_compositor:pdf_compositor_manifest",
+    "//components/services/pdf_compositor/public/interfaces",
     "//content/public/app:both",
     "//content/public/child:child",
     "//content/public/common",
@@ -726,6 +716,8 @@
     sources += [
       "lib/browser/headless_content_browser_client.cc",
       "lib/browser/headless_content_browser_client.h",
+      "lib/browser/headless_overlay_manifests.cc",
+      "lib/browser/headless_overlay_manifests.h",
       "lib/utility/headless_content_utility_client.cc",
       "lib/utility/headless_content_utility_client.h",
     ]
@@ -735,6 +727,8 @@
     ":headless_renderer",
     "//base",
     "//components/security_state/content",
+    "//components/services/pdf_compositor:pdf_compositor_manifest",
+    "//components/services/pdf_compositor/public/interfaces",
     "//content/test:test_support",
     "//services/network/public/mojom",
     "//testing/gmock",
@@ -781,10 +775,14 @@
       "app/headless_shell_win.cc",
       "lib/browser/headless_content_browser_client.cc",
       "lib/browser/headless_content_browser_client.h",
+      "lib/browser/headless_overlay_manifests.cc",
+      "lib/browser/headless_overlay_manifests.h",
       "public/headless_shell.h",
     ]
     deps = [
       ":headless",
+      "//components/services/pdf_compositor:pdf_compositor_manifest",
+      "//components/services/pdf_compositor/public/interfaces",
       "//content:sandbox_helper_win",
       "//content/public/browser",
       "//content/public/common",
@@ -860,6 +858,8 @@
     sources += [
       "lib/browser/headless_content_browser_client.cc",
       "lib/browser/headless_content_browser_client.h",
+      "lib/browser/headless_overlay_manifests.cc",
+      "lib/browser/headless_overlay_manifests.h",
       "lib/utility/headless_content_utility_client.cc",
       "lib/utility/headless_content_utility_client.h",
     ]
@@ -869,6 +869,8 @@
     ":headless_renderer",
     "//components/os_crypt",
     "//components/security_state/content",
+    "//components/services/pdf_compositor:pdf_compositor_manifest",
+    "//components/services/pdf_compositor/public/interfaces",
     "//content/public/app:both",
     "//content/public/browser",
     "//content/public/child:child",
diff --git a/headless/lib/browser/DEPS b/headless/lib/browser/DEPS
index a806690..1c3f6ca 100644
--- a/headless/lib/browser/DEPS
+++ b/headless/lib/browser/DEPS
@@ -3,6 +3,7 @@
   "+components/printing/browser",
   "+components/printing/common",
   "+components/security_state",
+  "+components/services/pdf_compositor/pdf_compositor_manifest.h",
   "+components/viz",
   "+printing",
   "+services/network",
diff --git a/headless/lib/browser/OWNERS b/headless/lib/browser/OWNERS
index d2386d8..6640f18 100644
--- a/headless/lib/browser/OWNERS
+++ b/headless/lib/browser/OWNERS
@@ -1,4 +1,4 @@
-per-file headless_browser_manifest_overlay.json=set noparent
-per-file headless_browser_manifest_overlay.json=file://ipc/SECURITY_OWNERS
-per-file headless_packaged_services_manifest_overlay.json=set noparent
-per-file headless_packaged_services_manifest_overlay.json=file://ipc/SECURITY_OWNERS
+per-file headless_overlay_manifests.cc=set noparent
+per-file headless_overlay_manifests.cc=file://ipc/SECURITY_OWNERS
+per-file headless_overlay_manifests.h=set noparent
+per-file headless_overlay_manifests.h=file://ipc/SECURITY_OWNERS
diff --git a/headless/lib/browser/headless_browser_manifest_overlay.json b/headless/lib/browser/headless_browser_manifest_overlay.json
deleted file mode 100644
index 80bc3f7..0000000
--- a/headless/lib/browser/headless_browser_manifest_overlay.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "name": "content_browser",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "requires": {
-        "pdf_compositor": [ "compositor" ]
-      }
-    },
-    "navigation:frame": {
-      "provides": {
-        "renderer": []
-      }
-    }
-  }
-}
diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc
index a8a32a3..8bbae2f5 100644
--- a/headless/lib/browser/headless_content_browser_client.cc
+++ b/headless/lib/browser/headless_content_browser_client.cc
@@ -10,7 +10,6 @@
 #include "base/base_switches.h"
 #include "base/callback.h"
 #include "base/command_line.h"
-#include "base/json/json_reader.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
@@ -24,18 +23,17 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/service_names.mojom.h"
 #include "headless/app/headless_shell_switches.h"
-#include "headless/grit/headless_lib_resources.h"
 #include "headless/lib/browser/headless_browser_context_impl.h"
 #include "headless/lib/browser/headless_browser_impl.h"
 #include "headless/lib/browser/headless_browser_main_parts.h"
 #include "headless/lib/browser/headless_devtools_manager_delegate.h"
+#include "headless/lib/browser/headless_overlay_manifests.h"
 #include "headless/lib/browser/headless_quota_permission_context.h"
 #include "headless/lib/headless_macros.h"
 #include "net/base/url_util.h"
 #include "net/ssl/client_cert_identity.h"
 #include "printing/buildflags/buildflags.h"
 #include "storage/browser/quota/quota_settings.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/gfx/switches.h"
 
@@ -150,20 +148,11 @@
 base::Optional<service_manager::Manifest>
 HeadlessContentBrowserClient::GetServiceManifestOverlay(
     base::StringPiece name) {
-  if (name == content::mojom::kBrowserServiceName) {
-    return service_manager::Manifest::FromValueDeprecated(
-        GetBrowserServiceManifestOverlay());
-  }
+  if (name == content::mojom::kBrowserServiceName)
+    return GetHeadlessContentBrowserOverlayManifest();
 
-  if (name == content::mojom::kRendererServiceName) {
-    return service_manager::Manifest::FromValueDeprecated(
-        GetRendererServiceManifestOverlay());
-  }
-
-  if (name == content::mojom::kPackagedServicesServiceName) {
-    return service_manager::Manifest::FromValueDeprecated(
-        GetPackagedServicesServiceManifestOverlay());
-  }
+  if (name == content::mojom::kPackagedServicesServiceName)
+    return GetHeadlessContentPackagedServicesOverlayManifest();
 
   return base::nullopt;
 }
@@ -176,30 +165,6 @@
 #endif
 }
 
-std::unique_ptr<base::Value>
-HeadlessContentBrowserClient::GetBrowserServiceManifestOverlay() {
-  base::StringPiece manifest_template =
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-          IDR_HEADLESS_BROWSER_MANIFEST_OVERLAY);
-  return base::JSONReader::Read(manifest_template);
-}
-
-std::unique_ptr<base::Value>
-HeadlessContentBrowserClient::GetRendererServiceManifestOverlay() {
-  base::StringPiece manifest_template =
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-          IDR_HEADLESS_RENDERER_MANIFEST_OVERLAY);
-  return base::JSONReader::Read(manifest_template);
-}
-
-std::unique_ptr<base::Value>
-HeadlessContentBrowserClient::GetPackagedServicesServiceManifestOverlay() {
-  base::StringPiece manifest_template =
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-          IDR_HEADLESS_PACKAGED_SERVICES_MANIFEST_OVERLAY);
-  return base::JSONReader::Read(manifest_template);
-}
-
 content::QuotaPermissionContext*
 HeadlessContentBrowserClient::CreateQuotaPermissionContext() {
   return new HeadlessQuotaPermissionContext();
diff --git a/headless/lib/browser/headless_overlay_manifests.cc b/headless/lib/browser/headless_overlay_manifests.cc
new file mode 100644
index 0000000..7aed11de
--- /dev/null
+++ b/headless/lib/browser/headless_overlay_manifests.cc
@@ -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.
+
+#include "headless/lib/browser/headless_overlay_manifests.h"
+
+#include "base/no_destructor.h"
+#include "components/services/pdf_compositor/pdf_compositor_manifest.h"
+#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+
+namespace headless {
+
+const service_manager::Manifest& GetHeadlessContentBrowserOverlayManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .RequireCapability(printing::mojom::kServiceName, "compositor")
+          .Build()};
+
+  return *manifest;
+}
+
+const service_manager::Manifest&
+GetHeadlessContentPackagedServicesOverlayManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .PackageService(pdf_compositor::GetManifest())
+          .Build()};
+
+  return *manifest;
+}
+
+}  // namespace headless
diff --git a/headless/lib/browser/headless_overlay_manifests.h b/headless/lib/browser/headless_overlay_manifests.h
new file mode 100644
index 0000000..710c326f
--- /dev/null
+++ b/headless/lib/browser/headless_overlay_manifests.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 HEADLESS_LIB_BROWSER_HEADLESS_OVERLAY_MANIFESTS_H_
+#define HEADLESS_LIB_BROWSER_HEADLESS_OVERLAY_MANIFESTS_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace headless {
+
+// Returns the manifest headless Chrome amends to Content's content_browser
+// service manifest. This allows headless Chrome to extend the capabilities
+// exposed and/or required by content_browser service instances.
+const service_manager::Manifest& GetHeadlessContentBrowserOverlayManifest();
+
+// Returns the manifest headless Chrome amends to Content's
+// content_packaged_services service manifest. This allows headless Chrome to
+// extend the set of in- and out-of- process services packaged by the browser.
+const service_manager::Manifest&
+GetHeadlessContentPackagedServicesOverlayManifest();
+
+}  // namespace headless
+
+#endif  // HEADLESS_LIB_BROWSER_HEADLESS_OVERLAY_MANIFESTS_H_
diff --git a/headless/lib/browser/headless_packaged_services_manifest_overlay.json b/headless/lib/browser/headless_packaged_services_manifest_overlay.json
deleted file mode 100644
index 2b560d9..0000000
--- a/headless/lib/browser/headless_packaged_services_manifest_overlay.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "content_packaged_services",
-  "interface_provider_specs": {}
-}
diff --git a/headless/lib/renderer/OWNERS b/headless/lib/renderer/OWNERS
deleted file mode 100644
index b81b2a7..0000000
--- a/headless/lib/renderer/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file headless_renderer_manifest_overlay.json=set noparent
-per-file headless_renderer_manifest_overlay.json=file://ipc/SECURITY_OWNERS
diff --git a/headless/lib/renderer/headless_renderer_manifest_overlay.json b/headless/lib/renderer/headless_renderer_manifest_overlay.json
deleted file mode 100644
index 9479ab9..0000000
--- a/headless/lib/renderer/headless_renderer_manifest_overlay.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "content_renderer",
-  "interface_provider_specs": {
-    "navigation:frame": {
-      "provides": {
-        "browser": [
-          "headless.HeadlessRenderFrameController"
-        ]
-      }
-    }
-  }
-}
diff --git a/headless/lib/resources/headless_lib_resources.grd b/headless/lib/resources/headless_lib_resources.grd
index 160af03..3137ec6 100644
--- a/headless/lib/resources/headless_lib_resources.grd
+++ b/headless/lib/resources/headless_lib_resources.grd
@@ -10,9 +10,6 @@
   <release seq="1">
     <includes>
       <include name="IDR_HEADLESS_LIB_DEVTOOLS_DISCOVERY_PAGE" file="devtools_discovery_page.html" type="BINDATA" />
-      <include name="IDR_HEADLESS_BROWSER_MANIFEST_OVERLAY" file="${mojom_root}/headless/headless_browser_manifest_overlay.json" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_HEADLESS_RENDERER_MANIFEST_OVERLAY" file="../renderer/headless_renderer_manifest_overlay.json" type="BINDATA" />
-      <include name="IDR_HEADLESS_PACKAGED_SERVICES_MANIFEST_OVERLAY" file="${mojom_root}/headless/headless_packaged_services_manifest_overlay.json" use_base_dir="false" type="BINDATA" />
     </includes>
   </release>
 </grit>
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index ed219ad..0f5f821 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -104,7 +104,9 @@
         path_regexp: "components/viz/.+"
         path_regexp: "content/test/gpu/.+"
         path_regexp: "gpu/.+"
-        path_regexp: "media/(audio|filters|gpu)/.+"
+        path_regexp: "media/audio/.+"
+        path_regexp: "media/filters/.+"
+        path_regexp: "media/gpu/.+"
         path_regexp: "services/viz/.+"
         path_regexp: "testing/trigger_scripts/.+"
         path_regexp: "third_party/blink/renderer/modules/webgl/.+"
@@ -128,11 +130,9 @@
       builders {
         name: "linux-blink-rel"
         path_regexp: "cc/.+"
-        path_regexp: "third_party/blink/renderer/core/(svg|paint)/.+"
-        path_regexp: "third_party/blink/renderer/core/paint/compositing/.+"
+        path_regexp: "third_party/blink/renderer/core/paint/.+"
+        path_regexp: "third_party/blink/renderer/core/svg/.+"
         path_regexp: "third_party/blink/renderer/platform/graphics/.+"
-        path_regexp: "third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees"
-        path_regexp: "third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees"
         path_regexp: "third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint"
         path_regexp: "third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+"
       }
@@ -142,11 +142,9 @@
       }
       builders {
         name: "linux_layout_tests_composite_after_paint"
-        path_regexp: "third_party/blink/renderer/core/paint/compositing/.+"
-        path_regexp: "third_party/blink/renderer/core/(svg|paint)/.+"
+        path_regexp: "third_party/blink/renderer/core/paint/.+"
+        path_regexp: "third_party/blink/renderer/core/svg/.+"
         path_regexp: "third_party/blink/renderer/platform/graphics/.+"
-        path_regexp: "third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees"
-        path_regexp: "third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees"
         path_regexp: "third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint"
         path_regexp: "third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/.+"
       }
@@ -172,7 +170,9 @@
         path_regexp: "chrome/browser/vr/.+"
         path_regexp: "content/test/gpu/.+"
         path_regexp: "gpu/.+"
-        path_regexp: "media/(audio|filters|gpu)/.+"
+        path_regexp: "media/audio/.+"
+        path_regexp: "media/filters/.+"
+        path_regexp: "media/gpu/.+"
         path_regexp: "testing/trigger_scripts/.+"
         path_regexp: "third_party/blink/renderer/modules/webgl/.+"
         path_regexp: "ui/gl/.+"
@@ -186,7 +186,9 @@
         path_regexp: "chrome/browser/vr/.+"
         path_regexp: "content/test/gpu/.+"
         path_regexp: "gpu/.+"
-        path_regexp: "media/(audio|filters|gpu)/.+"
+        path_regexp: "media/audio/.+"
+        path_regexp: "media/filters/.+"
+        path_regexp: "media/gpu/.+"
         path_regexp: "services/shape_detection/.+"
         path_regexp: "testing/trigger_scripts/.+"
         path_regexp: "third_party/blink/renderer/modules/webgl/.+"
@@ -198,7 +200,9 @@
         path_regexp: "content/test/gpu/.+"
         path_regexp: "device/vr/.+"
         path_regexp: "gpu/.+"
-        path_regexp: "media/(audio|filters|gpu)/.+"
+        path_regexp: "media/audio/.+"
+        path_regexp: "media/filters/.+"
+        path_regexp: "media/gpu/.+"
         path_regexp: "testing/trigger_scripts/.+"
         path_regexp: "third_party/blink/renderer/modules/vr/.+"
         path_regexp: "third_party/blink/renderer/modules/webgl/.+"
@@ -246,11 +250,17 @@
       name: "master.tryserver.chromium.android"
       builders {
         name: "android_compile_x64_dbg"
-        path_regexp: "sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/.+"
+        path_regexp: "sandbox/linux/seccomp-bpf/.+"
+        path_regexp: "sandbox/linux/seccomp-bpf-helpers/.+"
+        path_regexp: "sandbox/linux/system_headers/.+"
+        path_regexp: "sandbox/linux/tests/.+"
       }
       builders {
         name: "android_compile_x86_dbg"
-        path_regexp: "sandbox/linux/(bpd_dsl|seccomp-bpf|secomp-bpf-helpers|system_headers|tests)/.+"
+        path_regexp: "sandbox/linux/seccomp-bpf/.+"
+        path_regexp: "sandbox/linux/seccomp-bpf-helpers/.+"
+        path_regexp: "sandbox/linux/system_headers/.+"
+        path_regexp: "sandbox/linux/tests/.+"
       }
       builders {
         name: "android_cronet_tester"
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 2e03f56..542664d 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -10,23 +10,12 @@
 import("//ios/build/config.gni")
 import("//ios/public/provider/chrome/browser/build_config.gni")
 import("//ios/third_party/firebase/firebase.gni")
-import("//services/service_manager/public/service_manifest.gni")
 
 buildflag_header("firebase_buildflags") {
   header = "firebase_buildflags.h"
   flags = [ "FIREBASE_ENABLED=$ios_enable_firebase_sdk" ]
 }
 
-service_manifest("chrome_browser_manifest_overlay") {
-  source = "//ios/chrome/browser/chrome_browser_manifest_overlay.json"
-  packaged_services = [ "//services/identity:manifest" ]
-}
-
-service_manifest("chrome_packaged_services_manifest_overlay") {
-  source = "//ios/chrome/browser/chrome_packaged_services_manifest_overlay.json"
-  packaged_services = [ "//components/services/unzip:manifest" ]
-}
-
 source_set("app") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn
index f54de54..5ed1c6e 100644
--- a/ios/chrome/app/resources/BUILD.gn
+++ b/ios/chrome/app/resources/BUILD.gn
@@ -27,19 +27,10 @@
   # The .grd contains references to generated files.
   source_is_generated = true
 
-  grit_flags = [
-    "-E",
-    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
-  ]
-
   outputs = [
     "grit/ios_resources.h",
     "ios_resources.pak",
   ]
-  deps = [
-    "//ios/chrome/app:chrome_browser_manifest_overlay",
-    "//ios/chrome/app:chrome_packaged_services_manifest_overlay",
-  ]
 }
 
 group("packed_resources") {
diff --git a/ios/chrome/app/resources/ios_resources.grd b/ios/chrome/app/resources/ios_resources.grd
index c92dbf66..1aa2035 100644
--- a/ios/chrome/app/resources/ios_resources.grd
+++ b/ios/chrome/app/resources/ios_resources.grd
@@ -12,8 +12,6 @@
       <include name="IDR_IOS_INSPECT_JS" file="inspect/inspect.js" type="BINDATA" compress="gzip" />
       <include name="IDR_IOS_OMAHA_HTML" file="omaha/omaha.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
       <include name="IDR_IOS_OMAHA_JS" file="omaha/omaha.js" type="BINDATA" compress="gzip" />
-      <include name="IDR_CHROME_BROWSER_MANIFEST_OVERLAY" file="${root_gen_dir}/ios/chrome/app/chrome_browser_manifest_overlay.json" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_CHROME_PACKAGED_SERVICES_MANIFEST_OVERLAY" file="${root_gen_dir}/ios/chrome/app/chrome_packaged_services_manifest_overlay.json" use_base_dir="false" type="BINDATA" />
       <include name="IDR_IOS_UKM_INTERNALS_HTML" file="../../../../components/ukm/debug/ukm_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" />
       <include name="IDR_IOS_UKM_INTERNALS_JS" file="../../../../components/ukm/debug/ukm_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
     </includes>
diff --git a/ios/chrome/browser/OWNERS b/ios/chrome/browser/OWNERS
index 7cbfc15..1f4b984 100644
--- a/ios/chrome/browser/OWNERS
+++ b/ios/chrome/browser/OWNERS
@@ -8,8 +8,5 @@
 
 per-file ios_chrome_flag_descriptions.*=*
 
-per-file chrome_browser_manifest_overlay.json=set noparent
-per-file chrome_browser_manifest_overlay.json=file://ipc/SECURITY_OWNERS
-
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
diff --git a/ios/chrome/browser/chrome_browser_manifest_overlay.json b/ios/chrome/browser/chrome_browser_manifest_overlay.json
deleted file mode 100644
index 64a1ad97..0000000
--- a/ios/chrome/browser/chrome_browser_manifest_overlay.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "web_browser",
-  "display_name": "Chrome",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "requires": {
-        "identity": [ "identity_manager" ],
-        "unzip_service": [ "unzip_file" ]
-      }
-    }
-  }
-}
diff --git a/ios/chrome/browser/chrome_packaged_services_manifest_overlay.json b/ios/chrome/browser/chrome_packaged_services_manifest_overlay.json
deleted file mode 100644
index ae2424b..0000000
--- a/ios/chrome/browser/chrome_packaged_services_manifest_overlay.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "web_packaged_services",
-  "display_name": "Chrome Packaged Services"
-}
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_finder.h b/ios/chrome/browser/payments/ios_payment_instrument_finder.h
index 8ca9667..d90170f 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_finder.h
+++ b/ios/chrome/browser/payments/ios_payment_instrument_finder.h
@@ -107,6 +107,7 @@
   // and |method| is the url payment method identifier that corresponds with
   // this manifest.
   void OnPaymentManifestDownloaded(const GURL& method,
+                                   const GURL& method_url_after_redirects,
                                    const std::string& content);
 
   // Parses a payment method manifest for its default applications and gets all
@@ -122,9 +123,11 @@
   // the url payment method that corresponds with this manifest.
   // |web_app_manifest_url| is the web app manifest url. This is passed to
   // GetPaymentAppDetailsFromWebAppManifest for validating an icon source path.
-  void OnWebAppManifestDownloaded(const GURL& method,
-                                  const GURL& web_app_manifest_url,
-                                  const std::string& content);
+  void OnWebAppManifestDownloaded(
+      const GURL& method,
+      const GURL& web_app_manifest_url,
+      const GURL& web_app_manifest_url_after_redirects,
+      const std::string& content);
 
   // Parses a web app manifest for its name, icon, and universal link. |input|
   // is the json encoded web app manifest to parse. |web_app_manifest_url|
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_finder.mm b/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
index 56a416b1..4f514a4 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_finder.mm
@@ -125,6 +125,7 @@
 
 void IOSPaymentInstrumentFinder::OnPaymentManifestDownloaded(
     const GURL& method,
+    const GURL& method_url_after_redirects,
     const std::string& content) {
   // If |content| is empty then the download failed.
   if (content.empty()) {
@@ -212,6 +213,7 @@
 void IOSPaymentInstrumentFinder::OnWebAppManifestDownloaded(
     const GURL& method,
     const GURL& web_app_manifest_url,
+    const GURL& web_app_manifest_url_after_redirects,
     const std::string& content) {
   // If |content| is empty then the download failed.
   if (content.empty()) {
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_finder_unittest.mm b/ios/chrome/browser/payments/ios_payment_instrument_finder_unittest.mm
index 445cf77..b53283bc 100644
--- a/ios/chrome/browser/payments/ios_payment_instrument_finder_unittest.mm
+++ b/ios/chrome/browser/payments/ios_payment_instrument_finder_unittest.mm
@@ -45,8 +45,8 @@
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)),
         ios_payment_instrument_finder_(
-            std::make_unique<TestIOSPaymentInstrumentFinder>(
-                shared_factory_)) {}
+            std::make_unique<TestIOSPaymentInstrumentFinder>(shared_factory_)) {
+  }
 
   ~PaymentRequestIOSPaymentInstrumentFinderTest() override {}
 
@@ -139,8 +139,9 @@
         &PaymentRequestIOSPaymentInstrumentFinderTest::InstrumentsFoundCallback,
         base::Unretained(this));
     ios_payment_instrument_finder_->num_instruments_to_find_ = 1;
+    GURL web_app_manifest_url("https://bobpay.xyz/bob/manifest.json");
     ios_payment_instrument_finder_->OnWebAppManifestDownloaded(
-        method, GURL("https://bobpay.xyz/bob/manifest.json"), content);
+        method, web_app_manifest_url, web_app_manifest_url, content);
   }
 
   void RunLoop() {
@@ -228,9 +229,8 @@
 
 TEST_F(PaymentRequestIOSPaymentInstrumentFinderTest,
        DefaultApplicationsShouldHaveAbsoluteUrl) {
-  ExpectUnableToParsePaymentMethodManifest(
-      "{\"default_applications\": ["
-      "\"app.json\"]}");
+  ExpectUnableToParsePaymentMethodManifest("{\"default_applications\": ["
+                                           "\"app.json\"]}");
 }
 
 TEST_F(PaymentRequestIOSPaymentInstrumentFinderTest,
@@ -243,23 +243,21 @@
 
 TEST_F(PaymentRequestIOSPaymentInstrumentFinderTest,
        WellFormedPaymentMethodManifestWithApps) {
-  ExpectParsedPaymentMethodManifest(
-      "{\"default_applications\": ["
-      "\"https://bobpay.com/app.json\","
-      "\"https://alicepay.com/app.json\"]}",
-      {GURL("https://bobpay.com/app.json"),
-       GURL("https://alicepay.com/app.json")});
+  ExpectParsedPaymentMethodManifest("{\"default_applications\": ["
+                                    "\"https://bobpay.com/app.json\","
+                                    "\"https://alicepay.com/app.json\"]}",
+                                    {GURL("https://bobpay.com/app.json"),
+                                     GURL("https://alicepay.com/app.json")});
 }
 
 TEST_F(PaymentRequestIOSPaymentInstrumentFinderTest,
        WellFormedPaymentMethodManifestWithDuplicateApps) {
-  ExpectParsedPaymentMethodManifest(
-      "{\"default_applications\": ["
-      "\"https://bobpay.com/app.json\","
-      "\"https://bobpay.com/app.json\","
-      "\"https://alicepay.com/app.json\"]}",
-      {GURL("https://bobpay.com/app.json"),
-       GURL("https://alicepay.com/app.json")});
+  ExpectParsedPaymentMethodManifest("{\"default_applications\": ["
+                                    "\"https://bobpay.com/app.json\","
+                                    "\"https://bobpay.com/app.json\","
+                                    "\"https://alicepay.com/app.json\"]}",
+                                    {GURL("https://bobpay.com/app.json"),
+                                     GURL("https://alicepay.com/app.json")});
 }
 
 // Web app manifest parsing:
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 60108e74..43a1571 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -197,6 +197,8 @@
   sources = [
     "blocked_popup_tab_helper.h",
     "blocked_popup_tab_helper.mm",
+    "chrome_overlay_manifests.cc",
+    "chrome_overlay_manifests.h",
     "chrome_web_client.h",
     "chrome_web_client.mm",
     "print_tab_helper.h",
@@ -214,6 +216,7 @@
     "//components/prefs",
     "//components/resources",
     "//components/services/unzip:lib",
+    "//components/services/unzip:manifest",
     "//components/services/unzip/public/interfaces",
     "//components/strings",
     "//components/version_info",
@@ -236,6 +239,8 @@
     "//ios/web",
     "//ios/web/public",
     "//net",
+    "//services/identity:manifest",
+    "//services/identity/public/mojom",
     "//ui/base",
     "//ui/gfx",
     "//url",
diff --git a/ios/chrome/browser/web/DEPS b/ios/chrome/browser/web/DEPS
index 46d60a2..3cd4831 100644
--- a/ios/chrome/browser/web/DEPS
+++ b/ios/chrome/browser/web/DEPS
@@ -1,3 +1,7 @@
+include_rules = [
+  "+services/identity/manifest.h",
+]
+
 specific_include_rules = {
   # web::HttpServer is deprecated in favor of net::EmbeddedTestServer.
   # TODO:(crbug.com/891834) Remove this exception.
diff --git a/ios/chrome/browser/web/OWNERS b/ios/chrome/browser/web/OWNERS
index 1a6c73c5..31a22b3e 100644
--- a/ios/chrome/browser/web/OWNERS
+++ b/ios/chrome/browser/web/OWNERS
@@ -3,3 +3,8 @@
 
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
+
+per-file chrome_overlay_manifests.cc=set noparent
+per-file chrome_overlay_manifests.cc=file://ipc/SECURITY_OWNERS
+per-file chrome_overlay_manifests.h=set noparent
+per-file chrome_overlay_manifests.h=file://ipc/SECURITY_OWNERS
diff --git a/ios/chrome/browser/web/chrome_overlay_manifests.cc b/ios/chrome/browser/web/chrome_overlay_manifests.cc
new file mode 100644
index 0000000..e8e5353
--- /dev/null
+++ b/ios/chrome/browser/web/chrome_overlay_manifests.cc
@@ -0,0 +1,32 @@
+// 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 "ios/chrome/browser/web/chrome_overlay_manifests.h"
+
+#include "base/no_destructor.h"
+#include "components/services/unzip/manifest.h"
+#include "components/services/unzip/public/interfaces/constants.mojom.h"
+#include "services/identity/manifest.h"
+#include "services/identity/public/mojom/constants.mojom.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+
+const service_manager::Manifest& GetChromeWebBrowserOverlayManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .RequireCapability(identity::mojom::kServiceName, "identity_manager")
+          .RequireCapability(unzip::mojom::kServiceName, "unzip_file")
+          .PackageService(identity::GetManifest())
+          .Build()};
+
+  return *manifest;
+}
+
+const service_manager::Manifest& GetChromeWebPackagedServicesOverlayManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .PackageService(unzip_service::GetManifest())
+          .Build()};
+
+  return *manifest;
+}
diff --git a/ios/chrome/browser/web/chrome_overlay_manifests.h b/ios/chrome/browser/web/chrome_overlay_manifests.h
new file mode 100644
index 0000000..f43b3bbb
--- /dev/null
+++ b/ios/chrome/browser/web/chrome_overlay_manifests.h
@@ -0,0 +1,20 @@
+// 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_WEB_CHROME_OVERLAY_MANIFESTS_H_
+#define IOS_CHROME_BROWSER_WEB_CHROME_OVERLAY_MANIFESTS_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+// Returns the manifest Chrome amends to the web_browser service manifest. This
+// allows Chrome to extend the capabilities exposed and/or required by
+// web_browser service instances.
+const service_manager::Manifest& GetChromeWebBrowserOverlayManifest();
+
+// Returns the manifest Chrome amends to the web_packaged_services service
+// manifest. This allows Chrome to extend the set of in-process services
+// packaged by the browser.
+const service_manager::Manifest& GetChromeWebPackagedServicesOverlayManifest();
+
+#endif  // IOS_CHROME_BROWSER_WEB_CHROME_OVERLAY_MANIFESTS_H_
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 806d84ab..118afcc 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/files/file_util.h"
-#include "base/json/json_reader.h"
 #include "base/mac/bundle_locations.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/dom_distiller/core/url_constants.h"
@@ -25,8 +24,8 @@
 #include "ios/chrome/browser/passwords/password_manager_features.h"
 #include "ios/chrome/browser/ssl/ios_ssl_error_handler.h"
 #import "ios/chrome/browser/ui/chrome_web_view_factory.h"
+#include "ios/chrome/browser/web/chrome_overlay_manifests.h"
 #import "ios/chrome/browser/web/error_page_util.h"
-#include "ios/chrome/grit/ios_resources.h"
 #include "ios/public/provider/chrome/browser/browser_url_rewriter_provider.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/public/provider/chrome/browser/voice/audio_session_controller.h"
@@ -151,20 +150,11 @@
 
 base::Optional<service_manager::Manifest>
 ChromeWebClient::GetServiceManifestOverlay(base::StringPiece name) {
-  int identifier = -1;
   if (name == web::mojom::kBrowserServiceName)
-    identifier = IDR_CHROME_BROWSER_MANIFEST_OVERLAY;
-  else if (name == web::mojom::kPackagedServicesServiceName)
-    identifier = IDR_CHROME_PACKAGED_SERVICES_MANIFEST_OVERLAY;
-
-  if (identifier == -1)
-    return base::nullopt;
-
-  base::StringPiece manifest_contents =
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
-          identifier, ui::ScaleFactor::SCALE_FACTOR_NONE);
-  return service_manager::Manifest::FromValueDeprecated(
-      base::JSONReader::Read(manifest_contents));
+    return GetChromeWebBrowserOverlayManifest();
+  if (name == web::mojom::kPackagedServicesServiceName)
+    return GetChromeWebPackagedServicesOverlayManifest();
+  return base::nullopt;
 }
 
 void ChromeWebClient::GetAdditionalWebUISchemes(
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 2d19ce1..783e311 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -37,6 +37,7 @@
     ":js_resources",
     ":navigation_resources",
     ":resources",
+    ":service_names",
     "//base",
     "//ios/web/download",
     "//ios/web/interstitials",
@@ -52,6 +53,7 @@
     "//services/network/public/mojom",
     "//services/service_manager",
     "//services/service_manager/public/cpp",
+    "//services/service_manager/public/mojom",
   ]
 
   sources = [
@@ -67,8 +69,12 @@
     "service_manager_context.mm",
     "url_scheme_util.mm",
     "url_util.cc",
+    "web_browser_manifest.h",
+    "web_browser_manifest.mm",
     "web_client.mm",
     "web_kit_constants.cc",
+    "web_packaged_services_manifest.h",
+    "web_packaged_services_manifest.mm",
     "web_sub_thread.cc",
     "web_sub_thread.h",
     "web_thread_impl.cc",
@@ -693,8 +699,6 @@
     "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
   ]
   deps = [
-    "//ios/web/public/app:browser_manifest",
-    "//ios/web/public/app:packaged_services_manifest",
     "//mojo/public/js:bindings",
   ]
 }
diff --git a/ios/web/OWNERS b/ios/web/OWNERS
index a7206ed..addd6dd 100644
--- a/ios/web/OWNERS
+++ b/ios/web/OWNERS
@@ -4,3 +4,12 @@
 
 # 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
+per-file web_packaged_services_manifest.cc=set noparent
+per-file web_packaged_services_manifest.cc=file://ipc/SECURITY_OWNERS
+per-file web_packaged_services_manifest.h=set noparent
+per-file web_packaged_services_manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/ios/web/ios_web_resources.grd b/ios/web/ios_web_resources.grd
index 2adab0b..cee472b 100644
--- a/ios/web/ios_web_resources.grd
+++ b/ios/web/ios_web_resources.grd
@@ -10,8 +10,6 @@
   <release seq="1">
     <includes>
       <include name="IDR_IOS_MOJO_BINDINGS_JS" file="${root_gen_dir}/mojo/public/js/mojo_bindings.js" flattenhtml="true" type="BINDATA" use_base_dir="false" compress="gzip" />
-      <include name="IDR_MOJO_WEB_BROWSER_MANIFEST" file="${root_gen_dir}/ios/web/public/app/browser_manifest.json" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_MOJO_WEB_PACKAGED_SERVICES_MANIFEST" file="${root_gen_dir}/ios/web/public/app/packaged_services_manifest.json" use_base_dir="false" type="BINDATA" />
     </includes>
   </release>
 </grit>
diff --git a/ios/web/public/app/BUILD.gn b/ios/web/public/app/BUILD.gn
index ea97a50..059e3f6 100644
--- a/ios/web/public/app/BUILD.gn
+++ b/ios/web/public/app/BUILD.gn
@@ -2,8 +2,6 @@
 # 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/service_manifest.gni")
-
 source_set("app") {
   sources = [
     "task_scheduler_init_params_callback.h",
@@ -31,13 +29,3 @@
 
   configs += [ "//build/config/compiler:enable_arc" ]
 }
-
-service_manifest("packaged_services_manifest") {
-  name = "web_packaged_services"
-  source = "mojo/web_packaged_services_manifest.json"
-}
-
-service_manifest("browser_manifest") {
-  name = "web_browser"
-  source = "mojo/web_browser_manifest.json"
-}
diff --git a/ios/web/public/app/mojo/OWNERS b/ios/web/public/app/mojo/OWNERS
deleted file mode 100644
index 547972a..0000000
--- a/ios/web/public/app/mojo/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# TEAM: ios-directory-owners@chromium.org
-# OS: iOS
-
-per-file web_browser_manifest.json=set noparent
-per-file web_browser_manifest.json=file://ipc/SECURITY_OWNERS
-
-per-file web_packaged_services_manifest.json=set noparent
-per-file web_packaged_services_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/ios/web/public/app/mojo/web_browser_manifest.json b/ios/web/public/app/mojo/web_browser_manifest.json
deleted file mode 100644
index 17dcedd3..0000000
--- a/ios/web/public/app/mojo/web_browser_manifest.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "name": "web_browser",
-  "display_name": "Web",
-  "options": {
-    "can_connect_to_other_services_as_any_user": true,
-    "can_connect_to_other_services_with_any_instance_name": true,
-    "can_create_other_service_instances": true
-  },
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "service_manager:service_factory": [
-          "service_manager.mojom.ServiceFactory"
-        ]
-      },
-      "requires": {
-        "*": [ "app" ],
-        "service_manager": [
-          "service_manager:service_manager"
-        ]
-      }
-    }
-  }
-}
diff --git a/ios/web/public/app/mojo/web_packaged_services_manifest.json b/ios/web/public/app/mojo/web_packaged_services_manifest.json
deleted file mode 100644
index 834699c..0000000
--- a/ios/web/public/app/mojo/web_packaged_services_manifest.json
+++ /dev/null
@@ -1,28 +0,0 @@
-// Primordial service for the browser process. This is a singleton service which
-// acts as a runtime ServiceFactory for other packaged global services exposed
-// at or below the web layer.
-//
-// Note that this is only for packaging services which do not require user
-// profile context. Services which require user profile context must instead be
-// packaged within the web_browser service.
-{
-  "name": "web_packaged_services",
-  "display_name": "Web Packaged Services",
-  "options" : {
-    "instance_sharing" : "shared_instance_across_users",
-    "can_connect_to_other_services_as_any_user": true,
-    "can_create_other_service_instances": true
-  },
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "service_manager:service_factory": [
-          "service_manager.mojom.ServiceFactory"
-        ]
-      },
-      "requires": {
-        "web_browser": []
-      }
-    }
-  }
-}
diff --git a/ios/web/public/service_names.mojom b/ios/web/public/service_names.mojom
index c24e128..409ac49d 100644
--- a/ios/web/public/service_names.mojom
+++ b/ios/web/public/service_names.mojom
@@ -5,11 +5,9 @@
 module web.mojom;
 
 // The default service name the browser identifies as when connecting to
-// the Service Manager. This must match the name in
-// src/ios/web/public/app/mojo/web_browser_manifest.json.
+// the Service Manager.
 const string kBrowserServiceName = "web_browser";
 
 // The service name used to identify the browser process's singleton service
-// instance which packages other browser-wide services. This must match the name
-// in src/ios/web/public/app/mojo/web_packaged_services_manifest.json.
+// instance which packages other browser-wide services.
 const string kPackagedServicesServiceName = "web_packaged_services";
diff --git a/ios/web/service_manager_context.mm b/ios/web/service_manager_context.mm
index bc4a51c6..4cbbc5b6 100644
--- a/ios/web/service_manager_context.mm
+++ b/ios/web/service_manager_context.mm
@@ -12,19 +12,19 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
-#include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
-#include "ios/web/grit/ios_web_resources.h"
 #include "ios/web/public/service_manager_connection.h"
 #include "ios/web/public/service_names.mojom.h"
 #include "ios/web/public/web_client.h"
 #include "ios/web/public/web_task_traits.h"
 #include "ios/web/public/web_thread.h"
 #include "ios/web/service_manager_connection_impl.h"
+#import "ios/web/web_browser_manifest.h"
+#import "ios/web/web_packaged_services_manifest.h"
 #include "services/catalog/public/mojom/constants.mojom.h"
 #include "services/service_manager/connect_params.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -47,26 +47,6 @@
   int resource_id;
 };
 
-service_manager::Manifest LoadServiceManifest(base::StringPiece service_name,
-                                              int resource_id) {
-  std::string contents =
-      GetWebClient()
-          ->GetDataResource(resource_id, ui::ScaleFactor::SCALE_FACTOR_NONE)
-          .as_string();
-  DCHECK(!contents.empty());
-
-  service_manager::Manifest manifest =
-      service_manager::Manifest::FromValueDeprecated(
-          base::JSONReader::Read(contents));
-
-  base::Optional<service_manager::Manifest> overlay =
-      GetWebClient()->GetServiceManifestOverlay(service_name);
-  if (overlay)
-    manifest.Amend(*overlay);
-
-  return manifest;
-}
-
 }  // namespace
 
 // State which lives on the IO thread and drives the ServiceManager.
@@ -125,12 +105,8 @@
 };
 
 ServiceManagerContext::ServiceManagerContext() {
-  std::vector<service_manager::Manifest> manifests = {
-      LoadServiceManifest(mojom::kBrowserServiceName,
-                          IDR_MOJO_WEB_BROWSER_MANIFEST),
-      LoadServiceManifest(mojom::kPackagedServicesServiceName,
-                          IDR_MOJO_WEB_PACKAGED_SERVICES_MANIFEST),
-  };
+  const std::vector<service_manager::Manifest> manifests = {
+      GetWebBrowserManifest(), GetWebPackagedServicesManifest()};
 
   in_process_context_ = base::MakeRefCounted<InProcessServiceManagerContext>();
   service_manager::mojom::ServicePtr packaged_services_service;
diff --git a/ios/web/shell/BUILD.gn b/ios/web/shell/BUILD.gn
index bbcfb32..cb45ca2e 100644
--- a/ios/web/shell/BUILD.gn
+++ b/ios/web/shell/BUILD.gn
@@ -34,53 +34,6 @@
   ]
 }
 
-service_manifest("shell_packaged_services_manifest_overlay") {
-  source = "web_shell_packaged_services_manifest_overlay.json"
-  packaged_services = [ "//services/test/echo:manifest" ]
-}
-
-service_manifest("shell_browser_manifest_overlay") {
-  source = "web_shell_browser_manifest_overlay.json"
-  packaged_services = [ "//services/test/user_id:manifest" ]
-}
-
-grit("resources") {
-  visibility = [ ":*" ]
-  source = "shell_resources.grd"
-
-  # The .grd contains references to generated files.
-  source_is_generated = true
-
-  grit_flags = [
-    "-E",
-    "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
-  ]
-  outputs = [
-    "grit/shell_resources.h",
-    "shell_resources.pak",
-  ]
-  deps = [
-    ":shell_browser_manifest_overlay",
-    ":shell_packaged_services_manifest_overlay",
-  ]
-}
-
-repack("packed_resources") {
-  visibility = [ ":shell" ]
-  sources = [
-    "$root_gen_dir/ios/web/ios_web_resources.pak",
-    "$root_gen_dir/ios/web/shell/shell_resources.pak",
-  ]
-  deps = [
-    "//ios/web:resources",
-  ]
-  public_deps = [
-    ":resources",
-  ]
-  output = "$target_gen_dir/web_shell_resources.pak"
-  copy_data_to_bundle = true
-}
-
 mojom("shell_interfaces") {
   sources = [
     "web_usage_controller.mojom",
@@ -109,7 +62,6 @@
   ]
 
   deps = [
-    ":packed_resources",
     ":shell_bundle_data",
     ":shell_interfaces",
     "//base",
@@ -121,8 +73,10 @@
     "//net:extras",
     "//services/service_manager/public/cpp",
     "//services/test/echo:lib",
+    "//services/test/echo:manifest",
     "//services/test/echo/public/mojom",
     "//services/test/user_id:lib",
+    "//services/test/user_id:manifest",
     "//services/test/user_id/public/mojom",
     "//ui/base",
   ]
diff --git a/ios/web/shell/OWNERS b/ios/web/shell/OWNERS
index 093c8a8..418d402 100644
--- a/ios/web/shell/OWNERS
+++ b/ios/web/shell/OWNERS
@@ -6,9 +6,3 @@
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
-
-per-file web_shell_browser_manifest_overlay.json=set noparent
-per-file web_shell_browser_manifest_overlay.json=file://ipc/SECURITY_OWNERS
-
-per-file web_shell_packaged_services_manifest_overlay.json=set noparent
-per-file web_shell_packaged_services_manifest_overlay.json=file://ipc/SECURITY_OWNERS
diff --git a/ios/web/shell/shell_resources.grd b/ios/web/shell/shell_resources.grd
deleted file mode 100644
index a3ae7f25..0000000
--- a/ios/web/shell/shell_resources.grd
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
-  <outputs>
-    <output filename="grit/shell_resources.h" type="rc_header">
-      <emit emit_type='prepend'></emit>
-    </output>
-    <output filename="shell_resources.pak" type="data_package" />
-  </outputs>
-  <translations />
-  <release seq="1">
-    <includes>
-      <include name="IDR_WEB_SHELL_BROWSER_MANIFEST_OVERLAY" file="${root_gen_dir}/ios/web/shell/shell_browser_manifest_overlay.json" use_base_dir="false" type="BINDATA" />
-      <include name="IDR_WEB_SHELL_PACKAGED_SERVICES_MANIFEST_OVERLAY" file="${root_gen_dir}/ios/web/shell/shell_packaged_services_manifest_overlay.json" use_base_dir="false" type="BINDATA" />
-    </includes>
-  </release>
-</grit>
diff --git a/ios/web/shell/shell_web_client.mm b/ios/web/shell/shell_web_client.mm
index 294da48..106a172 100644
--- a/ios/web/shell/shell_web_client.mm
+++ b/ios/web/shell/shell_web_client.mm
@@ -6,16 +6,17 @@
 
 #import <UIKit/UIKit.h>
 
-#include "base/json/json_reader.h"
 #include "ios/web/public/service_names.mojom.h"
 #include "ios/web/public/user_agent.h"
 #include "ios/web/public/web_state/web_state.h"
-#include "ios/web/shell/grit/shell_resources.h"
 #include "ios/web/shell/shell_web_main_parts.h"
 #import "ios/web/shell/web_usage_controller.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
 #include "services/test/echo/echo_service.h"
+#include "services/test/echo/manifest.h"
 #include "services/test/echo/public/mojom/echo.mojom.h"
+#include "services/test/user_id/manifest.h"
 #include "ui/base/resource/resource_bundle.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -99,20 +100,21 @@
 
 base::Optional<service_manager::Manifest>
 ShellWebClient::GetServiceManifestOverlay(base::StringPiece name) {
-  int identifier = -1;
-  if (name == mojom::kBrowserServiceName)
-    identifier = IDR_WEB_SHELL_BROWSER_MANIFEST_OVERLAY;
-  else if (name == mojom::kPackagedServicesServiceName)
-    identifier = IDR_WEB_SHELL_PACKAGED_SERVICES_MANIFEST_OVERLAY;
+  if (name == mojom::kBrowserServiceName) {
+    return service_manager::ManifestBuilder()
+        .RequireCapability(echo::mojom::kServiceName, "echo")
+        .RequireCapability("user_id", "user_id")
+        .PackageService(user_id::GetManifest())
+        .Build();
+  }
 
-  if (identifier == -1)
-    return base::nullopt;
+  if (name == mojom::kPackagedServicesServiceName) {
+    return service_manager::ManifestBuilder()
+        .PackageService(echo::GetManifest())
+        .Build();
+  }
 
-  base::StringPiece manifest_contents =
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
-          identifier, ui::ScaleFactor::SCALE_FACTOR_NONE);
-  return service_manager::Manifest::FromValueDeprecated(
-      base::JSONReader::Read(manifest_contents));
+  return base::nullopt;
 }
 
 void ShellWebClient::BindInterfaceRequestFromMainFrame(
diff --git a/ios/web/shell/web_shell_browser_manifest_overlay.json b/ios/web/shell/web_shell_browser_manifest_overlay.json
deleted file mode 100644
index ac88963..0000000
--- a/ios/web/shell/web_shell_browser_manifest_overlay.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "name": "web_browser",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "requires": {
-        "echo": [ "echo" ],
-        "user_id": [ "user_id" ]
-      }
-    }
-  }
-}
diff --git a/ios/web/shell/web_shell_packaged_services_manifest_overlay.json b/ios/web/shell/web_shell_packaged_services_manifest_overlay.json
deleted file mode 100644
index ba3717be..0000000
--- a/ios/web/shell/web_shell_packaged_services_manifest_overlay.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "name": "web_packaged_services",
-  "display_name": "Web Shell Packaged Services",
-  "interface_provider_specs": {}
-}
diff --git a/ios/web/web_browser_manifest.h b/ios/web/web_browser_manifest.h
new file mode 100644
index 0000000..5b7184cf
--- /dev/null
+++ b/ios/web/web_browser_manifest.h
@@ -0,0 +1,20 @@
+// 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_WEB_WEB_BROWSER_MANIFEST_H_
+#define IOS_WEB_WEB_BROWSER_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace web {
+
+// Returns the service manifest for the web_browser service, which is the main
+// service that the core browser identifies as. This determines what service
+// capabilities the browser has access to, as well as what per-profile services
+// are packaged within the browser.
+const service_manager::Manifest& GetWebBrowserManifest();
+
+}  // namespace web
+
+#endif  // IOS_WEB_WEB_BROWSER_MANIFEST_H_
diff --git a/ios/web/web_browser_manifest.mm b/ios/web/web_browser_manifest.mm
new file mode 100644
index 0000000..4d931f2
--- /dev/null
+++ b/ios/web/web_browser_manifest.mm
@@ -0,0 +1,44 @@
+// 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/web/web_browser_manifest.h"
+
+#include "base/no_destructor.h"
+#include "ios/web/public/service_names.mojom.h"
+#include "ios/web/public/web_client.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_factory.mojom.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+const service_manager::Manifest& GetWebBrowserManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .WithServiceName(mojom::kBrowserServiceName)
+          .WithDisplayName("Web")
+          .WithOptions(service_manager::ManifestOptionsBuilder()
+                           .CanConnectToInstancesInAnyGroup(true)
+                           .CanConnectToInstancesWithAnyId(true)
+                           .CanRegisterOtherServiceInstances(true)
+                           .Build())
+          .ExposeCapability("service_manager:service_factory",
+                            service_manager::Manifest::InterfaceList<
+                                service_manager::mojom::ServiceFactory>())
+          .RequireCapability(mojom::kBrowserServiceName, "")
+          .RequireCapability(service_manager::mojom::kServiceName,
+                             "service_manager:service_manager")
+          .Build()
+          .Amend(GetWebClient()
+                     ->GetServiceManifestOverlay(mojom::kBrowserServiceName)
+                     .value_or(service_manager::Manifest()))};
+
+  return *manifest;
+}
+
+}  // namespace web
diff --git a/ios/web/web_packaged_services_manifest.h b/ios/web/web_packaged_services_manifest.h
new file mode 100644
index 0000000..e638dec
--- /dev/null
+++ b/ios/web/web_packaged_services_manifest.h
@@ -0,0 +1,19 @@
+// 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_WEB_WEB_PACKAGED_SERVICES_MANIFEST_H_
+#define IOS_WEB_WEB_PACKAGED_SERVICES_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace web {
+
+// Returns the service manifest for the web_packaged_services service. This
+// contains nested manifests for all other services available to the browser,
+// with the exception of per-profile services which are packaged by web_browser.
+const service_manager::Manifest& GetWebPackagedServicesManifest();
+
+}  // namespace web
+
+#endif  // IOS_WEB_WEB_PACKAGED_SERVICES_MANIFEST_H_
diff --git a/ios/web/web_packaged_services_manifest.mm b/ios/web/web_packaged_services_manifest.mm
new file mode 100644
index 0000000..fc5730d
--- /dev/null
+++ b/ios/web/web_packaged_services_manifest.mm
@@ -0,0 +1,45 @@
+// 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/web/web_browser_manifest.h"
+
+#include "base/no_destructor.h"
+#include "ios/web/public/service_names.mojom.h"
+#include "ios/web/public/web_client.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_factory.mojom.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+const service_manager::Manifest& GetWebPackagedServicesManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .WithServiceName(mojom::kPackagedServicesServiceName)
+          .WithDisplayName("Web")
+          .WithOptions(service_manager::ManifestOptionsBuilder()
+                           .WithInstanceSharingPolicy(
+                               service_manager::Manifest::
+                                   InstanceSharingPolicy::kSharedAcrossGroups)
+                           .CanConnectToInstancesInAnyGroup(true)
+                           .CanRegisterOtherServiceInstances(true)
+                           .Build())
+          .ExposeCapability("service_manager:service_factory",
+                            service_manager::Manifest::InterfaceList<
+                                service_manager::mojom::ServiceFactory>())
+          .RequireCapability(mojom::kBrowserServiceName, "")
+          .Build()
+          .Amend(GetWebClient()
+                     ->GetServiceManifestOverlay(
+                         mojom::kPackagedServicesServiceName)
+                     .value_or(service_manager::Manifest()))};
+
+  return *manifest;
+}
+
+}  // namespace web
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 852e6c3..c882322 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -46,6 +46,7 @@
 ios_web_view_public_headers = [
   "public/cwv_export.h",
   "public/cwv_favicon.h",
+  "public/cwv_flags.h",
   "public/cwv_html_element.h",
   "public/cwv_navigation_action.h",
   "public/cwv_navigation_delegate.h",
@@ -116,6 +117,8 @@
   "internal/content_settings/web_view_host_content_settings_map_factory.mm",
   "internal/cwv_favicon.mm",
   "internal/cwv_favicon_internal.h",
+  "internal/cwv_flags.mm",
+  "internal/cwv_flags_internal.h",
   "internal/cwv_html_element.mm",
   "internal/cwv_html_element_internal.h",
   "internal/cwv_navigation_action.mm",
@@ -269,6 +272,7 @@
   "//components/browser_sync",
   "//components/content_settings/core/browser",
   "//components/flags_ui",
+  "//components/flags_ui:switches",
   "//components/gcm_driver",
   "//components/history/core/common",
   "//components/image_fetcher/ios",
@@ -400,6 +404,7 @@
     "internal/autofill/cwv_credit_card_verifier_unittest.mm",
     "internal/cwv_download_task_unittest.mm",
     "internal/cwv_favicon_unittest.mm",
+    "internal/cwv_flags_unittest.mm",
     "internal/cwv_html_element_unittest.mm",
     "internal/cwv_preferences_unittest.mm",
     "internal/cwv_preview_element_info_unittest.mm",
diff --git a/ios/web_view/internal/app/application_context.mm b/ios/web_view/internal/app/application_context.mm
index 5902152b..dfbc505 100644
--- a/ios/web_view/internal/app/application_context.mm
+++ b/ios/web_view/internal/app/application_context.mm
@@ -19,6 +19,7 @@
 #include "ios/web/public/web_thread.h"
 #include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/web_view_io_thread.h"
+#import "ios/web_view/internal/cwv_flags_internal.h"
 #include "net/socket/client_socket_pool_manager.h"
 #include "services/network/network_change_manager.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
diff --git a/ios/web_view/internal/cwv_flags.mm b/ios/web_view/internal/cwv_flags.mm
new file mode 100644
index 0000000..380139a
--- /dev/null
+++ b/ios/web_view/internal/cwv_flags.mm
@@ -0,0 +1,174 @@
+// 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/web_view/internal/cwv_flags_internal.h"
+
+#include <memory>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "components/flags_ui/feature_entry.h"
+#include "components/flags_ui/feature_entry_macros.h"
+#include "components/flags_ui/flags_state.h"
+#include "components/flags_ui/flags_storage.h"
+#include "components/flags_ui/flags_ui_switches.h"
+#include "components/flags_ui/pref_service_flags_storage.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
+#include "ios/web_view/internal/app/application_context.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+// Never skip an entry when getting feature entries.
+bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
+  return false;
+}
+
+const char kUseSyncSandboxFlagName[] = "use-sync-sandbox";
+const char kUseWalletSandboxFlagName[] = "use-wallet-sandbox";
+const char kUseWalletSandboxFlagNameEnabled[] = "use-wallet-sandbox@1";
+const char kUseWalletSandboxFlagNameDisabled[] = "use-wallet-sandbox@2";
+
+// |visible_name| and |visible_description| are not defined because
+// //ios/web_view exposes these flags view CWVFlags public API instead of
+// through an about:flags page.
+const flags_ui::FeatureEntry kFeatureEntries[] = {
+    // Controls if sync connects to the sandbox server instead of production.
+    {kUseSyncSandboxFlagName, /*visible_name=*/"", /*visible_description=*/"",
+     flags_ui::kOsIos,
+     SINGLE_VALUE_TYPE_AND_VALUE(
+         switches::kSyncServiceURL,
+         "https://chrome-sync.sandbox.google.com/chrome-sync/alpha")},
+    // Controls if wallet connects to the sandbox server instead of production.
+    {kUseWalletSandboxFlagName, /*visible_name=*/"", /*visible_description=*/"",
+     flags_ui::kOsIos,
+     ENABLE_DISABLE_VALUE_TYPE_AND_VALUE(
+         autofill::switches::kWalletServiceUseSandbox,
+         "1",
+         autofill::switches::kWalletServiceUseSandbox,
+         "0")},
+};
+
+}  // namespace ios_web_view
+
+@implementation CWVFlags {
+  PrefService* _prefService;
+  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> _flagsStorage;
+  std::unique_ptr<flags_ui::FlagsState> _flagsState;
+}
+
++ (instancetype)sharedInstance {
+  static CWVFlags* flags;
+  static dispatch_once_t onceToken;
+  dispatch_once(&onceToken, ^{
+    flags = [[CWVFlags alloc]
+        initWithPrefService:ios_web_view::ApplicationContext::GetInstance()
+                                ->GetLocalState()];
+  });
+  return flags;
+}
+
+- (instancetype)initWithPrefService:(PrefService*)prefService {
+  self = [super init];
+  if (self) {
+    _prefService = prefService;
+    _flagsStorage =
+        std::make_unique<flags_ui::PrefServiceFlagsStorage>(_prefService);
+    _flagsState = std::make_unique<flags_ui::FlagsState>(
+        ios_web_view::kFeatureEntries,
+        base::size(ios_web_view::kFeatureEntries));
+  }
+  return self;
+}
+
+#pragma mark - Public
+
+- (void)setUsesSyncAndWalletSandbox:(BOOL)usesSyncAndWalletSandbox {
+  _flagsState->SetFeatureEntryEnabled(_flagsStorage.get(),
+                                      ios_web_view::kUseSyncSandboxFlagName,
+                                      usesSyncAndWalletSandbox);
+
+  _flagsState->SetFeatureEntryEnabled(
+      _flagsStorage.get(),
+      usesSyncAndWalletSandbox
+          ? ios_web_view::kUseWalletSandboxFlagNameEnabled
+          : ios_web_view::kUseWalletSandboxFlagNameDisabled,
+      true);
+
+  _flagsStorage->CommitPendingWrites();
+}
+
+- (BOOL)usesSyncAndWalletSandbox {
+  BOOL usesSyncSandbox = NO;
+  BOOL usesWalletSandbox = NO;
+
+  base::ListValue supportedFeatures;
+  base::ListValue unsupportedFeatures;
+
+  _flagsState->GetFlagFeatureEntries(
+      _flagsStorage.get(), flags_ui::kGeneralAccessFlagsOnly,
+      &supportedFeatures, &unsupportedFeatures,
+      base::BindRepeating(&ios_web_view::SkipConditionalFeatureEntry));
+  for (size_t i = 0; i < supportedFeatures.GetSize(); i++) {
+    base::DictionaryValue* featureEntry;
+    if (!supportedFeatures.GetDictionary(i, &featureEntry)) {
+      NOTREACHED();
+    }
+    std::string internalName;
+    if (!featureEntry->GetString("internal_name", &internalName)) {
+      NOTREACHED();
+    }
+    if (internalName == ios_web_view::kUseSyncSandboxFlagName) {
+      bool enabled;
+      if (!featureEntry->GetBoolean("enabled", &enabled)) {
+        NOTREACHED();
+      }
+      usesSyncSandbox = enabled;
+    } else if (internalName == ios_web_view::kUseWalletSandboxFlagName) {
+      base::ListValue* options;
+      if (!featureEntry->GetList("options", &options)) {
+        NOTREACHED();
+      }
+      for (size_t j = 0; j < options->GetSize(); j++) {
+        base::DictionaryValue* option;
+        if (!options->GetDictionary(j, &option)) {
+          NOTREACHED();
+        }
+        std::string internalName;
+        if (!option->GetString("internal_name", &internalName)) {
+          NOTREACHED();
+        }
+        if (internalName == ios_web_view::kUseWalletSandboxFlagNameEnabled) {
+          bool selected;
+          if (!option->GetBoolean("selected", &selected)) {
+            NOTREACHED();
+          }
+          usesWalletSandbox = selected;
+        }
+      }
+    }
+  }
+  return usesSyncSandbox && usesWalletSandbox;
+}
+
+#pragma mark - Internal
+
+- (void)convertFlagsToCommandLineSwitches {
+  base::CommandLine* commandLine = base::CommandLine::ForCurrentProcess();
+  _flagsState->ConvertFlagsToSwitches(
+      _flagsStorage.get(), commandLine, flags_ui::kAddSentinels,
+      switches::kEnableFeatures, switches::kDisableFeatures);
+}
+
+@end
diff --git a/ios/web_view/internal/cwv_flags_internal.h b/ios/web_view/internal/cwv_flags_internal.h
new file mode 100644
index 0000000..ba68d88c
--- /dev/null
+++ b/ios/web_view/internal/cwv_flags_internal.h
@@ -0,0 +1,40 @@
+// 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_WEB_VIEW_INTERNAL_CWV_FLAGS_INTERNAL_H_
+#define IOS_WEB_VIEW_INTERNAL_CWV_FLAGS_INTERNAL_H_
+
+#import "ios/web_view/public/cwv_flags.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+namespace ios_web_view {
+// Flag name for whether or not sync sandbox is enabled.
+extern const char kUseSyncSandboxFlagName[];
+
+// Base flag name for whether or not wallet sandbox is enabled.
+// Wallet sandbox flag has three choices (default, enabled, disabled). To turn
+// this flag "on", the |kUseWalletSandboxFlagNameEnabled| needs to be enabled.
+// Conversely, to turn this flag "off", the |kUseWalletSandboxFlagNameDisabled|
+// needs to be enabled.
+extern const char kUseWalletSandboxFlagName[];
+extern const char kUseWalletSandboxFlagNameEnabled[];
+extern const char kUseWalletSandboxFlagNameDisabled[];
+}  // namespace
+
+class PrefService;
+
+@interface CWVFlags ()
+
+- (instancetype)initWithPrefService:(PrefService*)prefService;
+
+// Converts the configured flags into command line switches consumable by other
+// features such as autofill and sync.
+- (void)convertFlagsToCommandLineSwitches;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_INTERNAL_CWV_FLAGS_INTERNAL_H_
diff --git a/ios/web_view/internal/cwv_flags_unittest.mm b/ios/web_view/internal/cwv_flags_unittest.mm
new file mode 100644
index 0000000..a134a71a
--- /dev/null
+++ b/ios/web_view/internal/cwv_flags_unittest.mm
@@ -0,0 +1,71 @@
+// 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/web_view/internal/cwv_flags_internal.h"
+
+#include <memory>
+#include <set>
+
+#include "base/test/scoped_task_environment.h"
+#include "components/flags_ui/pref_service_flags_storage.h"
+#include "components/prefs/in_memory_pref_store.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_service_factory.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+class CWVFlagsTest : public PlatformTest {
+ protected:
+  CWVFlagsTest() {
+    auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
+    flags_ui::PrefServiceFlagsStorage::RegisterPrefs(pref_registry.get());
+
+    scoped_refptr<PersistentPrefStore> pref_store = new InMemoryPrefStore();
+    PrefServiceFactory factory;
+    factory.set_user_prefs(pref_store);
+
+    pref_service_ = factory.Create(pref_registry.get());
+    flags_storage_ = std::make_unique<flags_ui::PrefServiceFlagsStorage>(
+        pref_service_.get());
+    flags_ = [[CWVFlags alloc] initWithPrefService:pref_service_.get()];
+  }
+
+  std::unique_ptr<PrefService> pref_service_;
+  std::unique_ptr<flags_ui::PrefServiceFlagsStorage> flags_storage_;
+  CWVFlags* flags_;
+};
+
+// Tests CWVFlags' usesSyncAndWalletSandbox setter.
+TEST_F(CWVFlagsTest, SetUsesSyncAndWalletSandbox) {
+  flags_.usesSyncAndWalletSandbox = YES;
+  std::set<std::string> stored_flags = flags_storage_->GetFlags();
+  EXPECT_NE(stored_flags.end(), stored_flags.find(kUseSyncSandboxFlagName));
+  EXPECT_NE(stored_flags.end(),
+            stored_flags.find(kUseWalletSandboxFlagNameEnabled));
+
+  flags_.usesSyncAndWalletSandbox = NO;
+  stored_flags = flags_storage_->GetFlags();
+  EXPECT_EQ(stored_flags.end(), stored_flags.find(kUseSyncSandboxFlagName));
+  EXPECT_EQ(stored_flags.end(),
+            stored_flags.find(kUseWalletSandboxFlagNameEnabled));
+}
+
+// Tests CWVFlag's usesSyncAndWalletSandbox getter.
+TEST_F(CWVFlagsTest, GetUsesSyncAndWalletSadnbox) {
+  flags_storage_->SetFlags(
+      {kUseSyncSandboxFlagName, kUseWalletSandboxFlagNameEnabled});
+  EXPECT_TRUE(flags_.usesSyncAndWalletSandbox);
+
+  flags_storage_->SetFlags({kUseWalletSandboxFlagNameDisabled});
+  EXPECT_FALSE(flags_.usesSyncAndWalletSandbox);
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/web_view_web_main_parts.mm b/ios/web_view/internal/web_view_web_main_parts.mm
index cbbfde0588..f829c2f 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -14,6 +14,7 @@
 #include "components/sync/driver/sync_driver_switches.h"
 #include "ios/web_view/cwv_web_view_buildflags.h"
 #include "ios/web_view/internal/app/application_context.h"
+#import "ios/web_view/internal/cwv_flags_internal.h"
 #import "ios/web_view/internal/cwv_web_view_configuration_internal.h"
 #include "ios/web_view/internal/translate/web_view_translate_service.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -42,6 +43,9 @@
   PrefService* local_state = ApplicationContext::GetInstance()->GetLocalState();
   DCHECK(local_state);
 
+  // Flags are converted here to ensure it is set before being read by others.
+  [[CWVFlags sharedInstance] convertFlagsToCommandLineSwitches];
+
   ApplicationContext::GetInstance()->PreCreateThreads();
 
 #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
diff --git a/ios/web_view/public/cwv_flags.h b/ios/web_view/public/cwv_flags.h
new file mode 100644
index 0000000..927e2e8
--- /dev/null
+++ b/ios/web_view/public/cwv_flags.h
@@ -0,0 +1,32 @@
+// 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_WEB_VIEW_PUBLIC_CWV_FLAGS_H_
+#define IOS_WEB_VIEW_PUBLIC_CWV_FLAGS_H_
+
+#import <Foundation/Foundation.h>
+
+#import "cwv_export.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+CWV_EXPORT
+// Used to configure feature flags during runtime. Flags are persisted across
+// app restarts.
+@interface CWVFlags : NSObject
+
+// Whether or not sync and wallet features are communicating with the sandbox
+// servers. This can be used for testing purposes. The app must be restarted for
+// changes to take effect. Default value is NO.
+@property(nonatomic, assign) BOOL usesSyncAndWalletSandbox;
+
++ (instancetype)sharedInstance;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif  // IOS_WEB_VIEW_PUBLIC_CWV_FLAGS_H_
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m
index 04eaca3..3b8bda7c 100644
--- a/ios/web_view/shell/shell_view_controller.m
+++ b/ios/web_view/shell/shell_view_controller.m
@@ -285,6 +285,17 @@
                                       .syncController stopSyncAndClearIdentity];
                             }]];
 
+  NSString* sandboxTitle = [CWVFlags sharedInstance].usesSyncAndWalletSandbox
+                               ? @"Use production sync/wallet"
+                               : @"Use sandbox sync/wallet";
+  [alertController
+      addAction:[UIAlertAction actionWithTitle:sandboxTitle
+                                         style:UIAlertActionStyleDefault
+                                       handler:^(UIAlertAction* action) {
+                                         [CWVFlags sharedInstance]
+                                             .usesSyncAndWalletSandbox ^= YES;
+                                       }]];
+
   [self presentViewController:alertController animated:YES completion:nil];
 }
 
diff --git a/media/gpu/v4l2/v4l2_decode_surface.h b/media/gpu/v4l2/v4l2_decode_surface.h
index 41719a97..2a620356 100644
--- a/media/gpu/v4l2/v4l2_decode_surface.h
+++ b/media/gpu/v4l2/v4l2_decode_surface.h
@@ -107,10 +107,12 @@
 
   void PrepareSetCtrls(struct v4l2_ext_controls* ctrls) const override;
   void PrepareQueueBuffer(struct v4l2_buffer* buffer) const override;
-  virtual uint64_t GetReferenceID() const override;
+  uint64_t GetReferenceID() const override;
   bool Submit() const override;
 
  private:
+  ~V4L2ConfigStoreDecodeSurface() override = default;
+
   // The configuration store of the input buffer.
   uint32_t config_store_;
 };
diff --git a/media/learning/common/labelled_example.h b/media/learning/common/labelled_example.h
index 63e4c1e..ee89586f 100644
--- a/media/learning/common/labelled_example.h
+++ b/media/learning/common/labelled_example.h
@@ -92,6 +92,7 @@
 
   // Provide the |i|-th example, over [0, size()).
   const LabelledExample& operator[](size_t i) const { return examples_[i]; }
+  LabelledExample& operator[](size_t i) { return examples_[i]; }
 
   // Return a copy of this data with duplicate entries merged.  Example weights
   // will be summed.
diff --git a/media/learning/common/learning_task.h b/media/learning/common/learning_task.h
index 223bb7c..474ca8d 100644
--- a/media/learning/common/learning_task.h
+++ b/media/learning/common/learning_task.h
@@ -88,8 +88,13 @@
   // okay if some of these are model-specific.
   // TODO(liberato): switch to base::DictionaryValue?
 
-  // Number of examples before we'll train a model.
-  size_t min_data_set_size = 10u;
+  // Maximum data set size until we start replacing examples.
+  size_t max_data_set_size = 100u;
+
+  // Fraction of examples that must be new before the task controller will train
+  // a new model.  Note that this is a fraction of the number of examples that
+  // we currently have, which might be less than |max_data_set_size|.
+  double min_new_data_fraction = 0.1;
 
   // Should the accuracy of this model be recorded to UMA?
   bool record_accuracy_via_uma = true;
diff --git a/media/learning/impl/learning_task_controller_impl.cc b/media/learning/impl/learning_task_controller_impl.cc
index af53091..47e86855 100644
--- a/media/learning/impl/learning_task_controller_impl.cc
+++ b/media/learning/impl/learning_task_controller_impl.cc
@@ -35,8 +35,16 @@
 LearningTaskControllerImpl::~LearningTaskControllerImpl() = default;
 
 void LearningTaskControllerImpl::AddExample(const LabelledExample& example) {
-  // TODO(liberato): do we ever trim older examples?
-  training_data_->push_back(example);
+  if (training_data_->size() >= task_.max_data_set_size) {
+    // Replace a random example.  We don't necessarily want to replace the
+    // oldest, since we don't necessarily want to enforce an ad-hoc recency
+    // constraint here.  That's a different issue.
+    (*training_data_)[rng()->Generate(training_data_->size())] = example;
+  } else {
+    training_data_->push_back(example);
+  }
+  // Either way, we have one more example that we haven't used for training yet.
+  num_untrained_examples_++;
 
   // Once we have a model, see if we'd get |example| correct.
   if (model_ && reporter_) {
@@ -48,22 +56,32 @@
     reporter_->GetPredictionCallback(observed).Run(predicted);
   }
 
-  // Train every time we get a multiple of |data_set_size|.
-  // TODO(liberato): weight might go up by more than one.
-  if ((training_data_->total_weight() % task_.min_data_set_size) != 0)
+  // Can't train more than one model concurrently.
+  if (training_is_in_progress_)
     return;
 
+  // Train every time we get enough new examples.  Note that this works even if
+  // we are replacing old examples rather than adding new ones.
+  double frac = ((double)num_untrained_examples_) / training_data_->size();
+  if (frac < task_.min_new_data_fraction)
+    return;
+
+  num_untrained_examples_ = 0;
+
+  // TODO(liberato): don't do this if one is in-flight.
   TrainedModelCB model_cb =
       base::BindOnce(&LearningTaskControllerImpl::OnModelTrained, AsWeakPtr());
-  // TODO(liberato): Post to a background task runner.
+  training_is_in_progress_ = true;
+  // Note that this copies the training data, so it's okay if we add more
+  // examples to our copy before this returns.
+  // TODO(liberato): Post to a background task runner, and bind |model_cb| to
+  // the current one.
   training_cb_.Run(*training_data_.get(), std::move(model_cb));
-
-  // TODO(liberato): replace |training_data_| and merge them once the model is
-  // trained.  Else, new examples will change the data during training.  For
-  // now, training is synchronous, so it's okay as it is.
 }
 
 void LearningTaskControllerImpl::OnModelTrained(std::unique_ptr<Model> model) {
+  DCHECK(training_is_in_progress_);
+  training_is_in_progress_ = false;
   model_ = std::move(model);
 }
 
diff --git a/media/learning/impl/learning_task_controller_impl.h b/media/learning/impl/learning_task_controller_impl.h
index fd1118e6..2df39e1 100644
--- a/media/learning/impl/learning_task_controller_impl.h
+++ b/media/learning/impl/learning_task_controller_impl.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "media/learning/impl/distribution_reporter.h"
 #include "media/learning/impl/learning_task_controller.h"
+#include "media/learning/impl/random_number_generator.h"
 #include "media/learning/impl/training_algorithm.h"
 
 namespace media {
@@ -21,6 +22,7 @@
 
 class COMPONENT_EXPORT(LEARNING_IMPL) LearningTaskControllerImpl
     : public LearningTaskController,
+      public HasRandomNumberGenerator,
       public base::SupportsWeakPtr<LearningTaskControllerImpl> {
  public:
   LearningTaskControllerImpl(
@@ -46,6 +48,13 @@
   // Most recently trained model, or null.
   std::unique_ptr<Model> model_;
 
+  // We don't want to have multiple models in flight.
+  bool training_is_in_progress_ = false;
+
+  // Number of examples in |training_data_| that haven't been used for training.
+  // This helps us decide when to train a new model.
+  int num_untrained_examples_ = 0;
+
   TrainingAlgorithmCB training_cb_;
 
   // Optional reporter for training accuracy.
diff --git a/media/learning/impl/learning_task_controller_impl_unittest.cc b/media/learning/impl/learning_task_controller_impl_unittest.cc
index 61d363dc..d999cbc1 100644
--- a/media/learning/impl/learning_task_controller_impl_unittest.cc
+++ b/media/learning/impl/learning_task_controller_impl_unittest.cc
@@ -34,7 +34,8 @@
   LearningTaskControllerImplTest()
       : predicted_target_(123), not_predicted_target_(456) {
     // Don't require too many training examples per report.
-    task_.min_data_set_size = 4;
+    task_.max_data_set_size = 20;
+    task_.min_new_data_fraction = 0.1;
 
     std::unique_ptr<FakeDistributionReporter> reporter =
         std::make_unique<FakeDistributionReporter>(task_);
@@ -64,6 +65,7 @@
     TargetValue target_;
   };
 
+  // Note that this trains models synchronously.
   void OnTrain(TrainingData training_data, TrainedModelCB model_cb) {
     num_models_++;
     std::move(model_cb).Run(std::make_unique<FakeModel>(predicted_target_));
@@ -85,32 +87,42 @@
 TEST_F(LearningTaskControllerImplTest, AddingExamplesTrainsModelAndReports) {
   LabelledExample example;
 
-  // Adding the first n-1 examples shouldn't cause it to train a model.
-  for (size_t i = 0; i < task_.min_data_set_size - 1; i++)
-    controller_->AddExample(example);
-  EXPECT_EQ(num_models_, 0);
-
-  // Adding one more example should train a model.
-  controller_->AddExample(example);
-  EXPECT_EQ(num_models_, 1);
-
-  // No results should be reported yet.
-  EXPECT_EQ(reporter_raw_->num_reported_, 0);
-  EXPECT_EQ(reporter_raw_->num_correct_, 0);
-
-  // Adding one more example should report results.
+  // Up to the first 1/training_fraction examples should train on each example.
+  // Make each of the examples agree on |predicted_target_|.
   example.target_value = predicted_target_;
-  controller_->AddExample(example);
-  EXPECT_EQ(num_models_, 1);
-  EXPECT_EQ(reporter_raw_->num_reported_, 1);
-  EXPECT_EQ(reporter_raw_->num_correct_, 1);
+  int count = static_cast<int>(1.0 / task_.min_new_data_fraction);
+  for (int i = 0; i < count; i++) {
+    controller_->AddExample(example);
+    EXPECT_EQ(num_models_, i + 1);
+    // All examples except the first should be reported as correct.  For the
+    // first, there's no model to test again.
+    EXPECT_EQ(reporter_raw_->num_reported_, i);
+    EXPECT_EQ(reporter_raw_->num_correct_, i);
+  }
+  // The next |count| should train every other one.
+  for (int i = 0; i < count; i++) {
+    controller_->AddExample(example);
+    EXPECT_EQ(num_models_, count + (i + 1) / 2);
+  }
 
-  // Adding a value that doesn't match should report one more attempt.
+  // The next |count| should be the same, since we've reached the max training
+  // set size.
+  for (int i = 0; i < count; i++) {
+    controller_->AddExample(example);
+    EXPECT_EQ(num_models_, count + count / 2 + (i + 1) / 2);
+  }
+
+  // We should have reported results for each except the first.  All of them
+  // should be correct, since there's only one target so far.
+  EXPECT_EQ(reporter_raw_->num_reported_, count * 3 - 1);
+  EXPECT_EQ(reporter_raw_->num_correct_, count * 3 - 1);
+
+  // Adding a value that doesn't match should report one more attempt, with an
+  // incorrect prediction.
   example.target_value = not_predicted_target_;
   controller_->AddExample(example);
-  EXPECT_EQ(num_models_, 1);
-  EXPECT_EQ(reporter_raw_->num_reported_, 2);
-  EXPECT_EQ(reporter_raw_->num_correct_, 1);  // Still 1.
+  EXPECT_EQ(reporter_raw_->num_reported_, count * 3);
+  EXPECT_EQ(reporter_raw_->num_correct_, count * 3 - 1);  // Unchanged.
 }
 
 }  // namespace learning
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 813a246..fc570e71 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -1740,11 +1740,17 @@
 
     switch (key_.host_resolver_source) {
       case HostResolverSource::ANY:
-        // Default to DnsTask (with allowed fallback to ProcTask for address
-        // queries). But if hostname appears to be an MDNS name (ends in
+        // Force address queries with canonname to use ProcTask to counter poor
+        // CNAME support in DnsTask. See https://crbug.com/872665
+        //
+        // Otherwise, default to DnsTask (with allowed fallback to ProcTask for
+        // address queries). But if hostname appears to be an MDNS name (ends in
         // *.local), go with ProcTask for address queries and MdnsTask for non-
         // address queries.
-        if (!ResemblesMulticastDNSName(key_.hostname)) {
+        if ((key_.host_resolver_flags & HOST_RESOLVER_CANONNAME) &&
+            IsAddressType(key_.dns_query_type)) {
+          StartProcTask();
+        } else if (!ResemblesMulticastDNSName(key_.hostname)) {
           StartDnsTask(IsAddressType(
               key_.dns_query_type) /* allow_fallback_resolution */);
         } else if (IsAddressType(key_.dns_query_type)) {
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index cc08987..4abc892 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -5923,6 +5923,7 @@
 
   HostResolver::ResolveHostParameters params;
   params.include_canonical_name = true;
+  params.source = HostResolverSource::DNS;
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       HostPortPair("alias", 80), NetLogWithSource(), params));
   ASSERT_THAT(response.result_error(), IsOk());
@@ -5944,6 +5945,7 @@
 
   HostResolver::ResolveHostParameters params;
   params.include_canonical_name = true;
+  params.source = HostResolverSource::DNS;
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       HostPortPair("alias", 80), NetLogWithSource(), params));
   ASSERT_FALSE(response.complete());
@@ -5965,6 +5967,7 @@
   HostResolver::ResolveHostParameters params;
   params.dns_query_type = DnsQueryType::A;
   params.include_canonical_name = true;
+  params.source = HostResolverSource::DNS;
   ResolveHostResponseHelper response(resolver_->CreateRequest(
       HostPortPair("alias", 80), NetLogWithSource(), params));
   ASSERT_THAT(response.result_error(), IsOk());
@@ -5972,6 +5975,29 @@
             "correct");
 }
 
+// Test that without specifying source, a request that would otherwise be
+// handled by DNS is sent to the system resolver if cannonname is requested.
+TEST_F(HostResolverImplDnsTest, CanonicalNameForcesProc) {
+  // Disable fallback to ensure system resolver is used directly, not via
+  // fallback.
+  set_allow_fallback_to_proctask(false);
+
+  proc_->AddRuleForAllFamilies("nx_succeed", "192.168.1.102",
+                               HOST_RESOLVER_CANONNAME, "canonical");
+  proc_->SignalMultiple(1u);
+
+  ChangeDnsConfig(CreateValidDnsConfig());
+
+  HostResolver::ResolveHostParameters params;
+  params.include_canonical_name = true;
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair("nx_succeed", 80), NetLogWithSource(), params));
+  ASSERT_THAT(response.result_error(), IsOk());
+
+  EXPECT_EQ(response.request()->GetAddressResults().value().canonical_name(),
+            "canonical");
+}
+
 TEST_F(HostResolverImplTest, ResolveLocalHostname) {
   AddressList addresses;
 
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index e5979cc..8082ebcb 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -45,6 +45,15 @@
   output = "$target_gen_dir/version.h"
 }
 
+source_set("host") {
+  public_deps = [
+    ":common",
+  ]
+  deps = [
+    "//remoting/host/file_transfer",
+  ]
+}
+
 # This must be a static library instead of a source set because
 # remoting_unittests requires that remoting_me2me_host.cc not be pulled in,
 # which in turn depends on remoting_me2me_host_static which isn't part of that
@@ -52,7 +61,7 @@
 #
 # TODO fix this, successful builds should not depend on static libraries
 # stripping code.
-static_library("host") {
+static_library("common") {
   sources = [
     "action_executor.cc",
     "action_executor.h",
@@ -84,6 +93,7 @@
     "chromoting_messages.h",
     "chromoting_param_traits.cc",
     "chromoting_param_traits.h",
+    "chromoting_param_traits_impl.h",
     "client_session.cc",
     "client_session.h",
     "client_session_control.h",
@@ -297,7 +307,7 @@
     "//media",
     "//remoting/base",
     "//remoting/base:authorization",
-    "//remoting/host/file_transfer",
+    "//remoting/host/file_transfer:common",
     "//remoting/host/input_monitor",
     "//remoting/host/security_key",
     "//remoting/protocol",
@@ -432,8 +442,9 @@
     "//testing/gtest",
   ]
   public_deps = [
-    ":host",
+    ":common",
     "//remoting/base:test_support",
+    "//remoting/host/file_transfer:test_support",
     "//third_party/libjingle_xmpp",
     "//third_party/protobuf:protobuf_lite",
     "//third_party/webrtc/modules/desktop_capture",
@@ -501,13 +512,13 @@
   configs += [ "//remoting/build/config:version" ]
 
   deps = [
-    ":host",
+    ":common",
     ":test_support",
     "//remoting/host/file_transfer:unit_tests",
     "//remoting/host/it2me:common",
     "//remoting/host/native_messaging",
     "//remoting/host/security_key:unit_tests",
-    "//remoting/host/setup",
+    "//remoting/host/setup:common",
     "//remoting/proto",
     "//remoting/resources",
     "//skia",
diff --git a/remoting/host/chromeos/BUILD.gn b/remoting/host/chromeos/BUILD.gn
index 98bf999..d6ae15d 100644
--- a/remoting/host/chromeos/BUILD.gn
+++ b/remoting/host/chromeos/BUILD.gn
@@ -47,7 +47,7 @@
   configs += [ "//remoting/build/config:version" ]
 
   deps = [
-    "//remoting/host",
+    "//remoting/host:common",
     "//remoting/host:test_support",
     "//remoting/proto",
     "//remoting/resources",
diff --git a/remoting/host/chromoting_messages.cc b/remoting/host/chromoting_messages.cc
index d966a20..2367eb0 100644
--- a/remoting/host/chromoting_messages.cc
+++ b/remoting/host/chromoting_messages.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "remoting/host/chromoting_param_traits_impl.h"
+
 // Get basic type definitions.
 #define IPC_MESSAGE_IMPL
 #include "remoting/host/chromoting_messages.h"
diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h
index 3747a37..8f57c329 100644
--- a/remoting/host/chromoting_messages.h
+++ b/remoting/host/chromoting_messages.h
@@ -16,9 +16,9 @@
 #include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/screen_resolution.h"
 #include "remoting/proto/action.pb.h"
-#include "remoting/proto/file_transfer.pb.h"
 #include "remoting/proto/process_stats.pb.h"
 #include "remoting/protocol/errors.h"
+#include "remoting/protocol/file_transfer_helpers.h"
 #include "remoting/protocol/transport.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
@@ -211,10 +211,12 @@
                     std::string /* serialized_packet */)
 
 // Informs the network process of the result of a file operation on the file
-// identified by |file_id|. If error is set, the file ID is no longer valid.
-IPC_MESSAGE_CONTROL(ChromotingDesktopNetworkMsg_FileResult,
-                    uint64_t /* file_id */,
-                    base::Optional<remoting::protocol::FileTransfer_Error>)
+// identified by |file_id|. If |result| is an error, the file ID is no longer
+// valid.
+IPC_MESSAGE_CONTROL(
+    ChromotingDesktopNetworkMsg_FileResult,
+    uint64_t /* file_id */,
+    remoting::protocol::FileTransferResult<remoting::Monostate> /* result */)
 
 //-----------------------------------------------------------------------------
 // Chromoting messages sent from the network to the desktop process.
diff --git a/remoting/host/chromoting_param_traits.cc b/remoting/host/chromoting_param_traits.cc
index f02436e8..99caf39 100644
--- a/remoting/host/chromoting_param_traits.cc
+++ b/remoting/host/chromoting_param_traits.cc
@@ -445,4 +445,22 @@
       base::StringPrintf("FileTransfer Error: %s", formatted.str().c_str()));
 }
 
+// remoting::Monostate
+
+// static
+void IPC::ParamTraits<remoting::Monostate>::Write(base::Pickle*,
+                                                  const param_type&) {}
+
+// static
+bool ParamTraits<remoting::Monostate>::Read(const base::Pickle*,
+                                            base::PickleIterator*,
+                                            param_type*) {
+  return true;
+}
+
+// static
+void ParamTraits<remoting::Monostate>::Log(const param_type&, std::string* l) {
+  l->append("()");
+}
+
 }  // namespace IPC
diff --git a/remoting/host/chromoting_param_traits.h b/remoting/host/chromoting_param_traits.h
index 5364c6e..be5bab0 100644
--- a/remoting/host/chromoting_param_traits.h
+++ b/remoting/host/chromoting_param_traits.h
@@ -9,6 +9,7 @@
 #include "ipc/ipc_param_traits.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
+#include "remoting/base/result.h"
 #include "remoting/host/desktop_environment_options.h"
 #include "remoting/host/screen_resolution.h"
 #include "remoting/proto/action.pb.h"
@@ -131,6 +132,26 @@
   static void Log(const param_type& p, std::string* l);
 };
 
+template <>
+struct ParamTraits<remoting::Monostate> {
+  typedef remoting::Monostate param_type;
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* p);
+  static void Log(const param_type& p, std::string* l);
+};
+
+template <typename SuccessType, typename ErrorType>
+struct ParamTraits<remoting::Result<SuccessType, ErrorType>> {
+  typedef remoting::Result<SuccessType, ErrorType> param_type;
+  static void Write(base::Pickle* m, const param_type& p);
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* p);
+  static void Log(const param_type& p, std::string* l);
+};
+
 }  // namespace IPC
 
 #endif  // REMOTING_HOST_CHROMOTING_PARAM_TRAITS_H_
diff --git a/remoting/host/chromoting_param_traits_impl.h b/remoting/host/chromoting_param_traits_impl.h
new file mode 100644
index 0000000..15c82ab
--- /dev/null
+++ b/remoting/host/chromoting_param_traits_impl.h
@@ -0,0 +1,60 @@
+// 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 REMOTING_HOST_CHROMOTING_PARAM_TRAITS_IMPL_H_
+#define REMOTING_HOST_CHROMOTING_PARAM_TRAITS_IMPL_H_
+
+#include "remoting/host/chromoting_param_traits.h"
+
+#include "ipc/ipc_message_utils.h"
+
+template <typename SuccessType, typename ErrorType>
+void IPC::ParamTraits<remoting::Result<SuccessType, ErrorType>>::Log(
+    const param_type& p,
+    std::string* l) {
+  if (p) {
+    l->append("success: ");
+    LogParam(p.success(), l);
+  } else {
+    l->append("error: ");
+    LogParam(p.error(), l);
+  }
+}
+
+template <typename SuccessType, typename ErrorType>
+bool IPC::ParamTraits<remoting::Result<SuccessType, ErrorType>>::Read(
+    const base::Pickle* m,
+    base::PickleIterator* iter,
+    param_type* p) {
+  bool is_success = false;
+  if (!ReadParam(m, iter, &is_success)) {
+    return false;
+  }
+  if (is_success) {
+    p->EmplaceSuccess();
+    if (!ReadParam(m, iter, &p->success())) {
+      return false;
+    }
+  } else {
+    p->EmplaceError();
+    if (!ReadParam(m, iter, &p->error())) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename SuccessType, typename ErrorType>
+void IPC::ParamTraits<remoting::Result<SuccessType, ErrorType>>::Write(
+    base::Pickle* m,
+    const param_type& p) {
+  WriteParam(m, p.is_success());
+  if (p) {
+    WriteParam(m, p.success());
+  } else {
+    WriteParam(m, p.error());
+  }
+}
+
+#endif  // REMOTING_HOST_CHROMOTING_PARAM_TRAITS_IMPL_H_
diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc
index 9ea3c1ef..3aee074 100644
--- a/remoting/host/desktop_session_agent.cc
+++ b/remoting/host/desktop_session_agent.cc
@@ -444,11 +444,11 @@
 
 void DesktopSessionAgent::OnResult(
     uint64_t file_id,
-    base::Optional<protocol::FileTransfer_Error> error) {
+    protocol::FileTransferResult<Monostate> result) {
   DCHECK(caller_task_runner_->BelongsToCurrentThread());
 
-  SendToNetwork(
-      std::make_unique<ChromotingDesktopNetworkMsg_FileResult>(file_id, error));
+  SendToNetwork(std::make_unique<ChromotingDesktopNetworkMsg_FileResult>(
+      file_id, std::move(result)));
 }
 
 mojo::ScopedMessagePipeHandle DesktopSessionAgent::Start(
diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h
index 81aac89..834e49a2 100644
--- a/remoting/host/desktop_session_agent.h
+++ b/remoting/host/desktop_session_agent.h
@@ -106,7 +106,7 @@
 
   // IpcFileOperations::ResultHandler implementation.
   void OnResult(std::uint64_t file_id,
-                base::Optional<protocol::FileTransfer_Error> error) override;
+                protocol::FileTransferResult<Monostate> result) override;
 
   // Creates desktop integration components and a connected IPC channel to be
   // used to access them. The client end of the channel is returned.
diff --git a/remoting/host/file_transfer/BUILD.gn b/remoting/host/file_transfer/BUILD.gn
index 949bcb9..25065b0e 100644
--- a/remoting/host/file_transfer/BUILD.gn
+++ b/remoting/host/file_transfer/BUILD.gn
@@ -5,6 +5,33 @@
 import("//remoting/build/config/remoting_build.gni")
 
 source_set("file_transfer") {
+  sources = [
+    "ensure_user_mac.cc",
+    "ensure_user_win.cc",
+    "get_desktop_directory.cc",
+    "get_desktop_directory.h",
+    "get_desktop_directory_win.cc",
+  ]
+
+  public_deps = [
+    ":common",
+  ]
+
+  deps = [
+    "//base",
+    "//remoting/protocol",
+  ]
+
+  if (!is_mac && !is_win) {
+    sources += [ "ensure_user_no_op.cc" ]
+  }
+
+  if (is_win) {
+    sources -= [ "get_desktop_directory.cc" ]
+  }
+}
+
+source_set("common") {
   public = [
     "file_operations.h",
     "file_transfer_message_handler.h",
@@ -16,6 +43,7 @@
   sources = [
     "buffered_file_writer.cc",
     "buffered_file_writer.h",
+    "ensure_user.h",
     "file_transfer_message_handler.cc",
     "ipc_file_operations.cc",
     "local_file_operations.cc",
@@ -28,19 +56,33 @@
   ]
 }
 
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "ensure_user_no_op.cc",
+    "fake_file_operations.cc",
+    "fake_file_operations.h",
+    "get_desktop_directory.cc",
+  ]
+  deps = [
+    ":common",
+    "//remoting/protocol",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
   sources = [
     "buffered_file_writer_unittest.cc",
-    "fake_file_operations.cc",
-    "fake_file_operations.h",
     "file_transfer_message_handler_unittest.cc",
     "local_file_operations_unittest.cc",
   ]
 
   deps = [
-    ":file_transfer",
+    ":common",
+    ":test_support",
     "//remoting/protocol",
     "//testing/gtest",
   ]
diff --git a/remoting/host/file_transfer/buffered_file_writer.cc b/remoting/host/file_transfer/buffered_file_writer.cc
index 5021665..3af58f8 100644
--- a/remoting/host/file_transfer/buffered_file_writer.cc
+++ b/remoting/host/file_transfer/buffered_file_writer.cc
@@ -60,10 +60,12 @@
 }
 
 void BufferedFileWriter::OnWriteFileResult(
-    base::Optional<protocol::FileTransfer_Error> error,
-    std::unique_ptr<FileOperations::Writer> writer) {
-  writer_ = std::move(writer);
-  OnWriteResult(std::move(error));
+    protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
+        result) {
+  OnWriteResult(std::move(result).Map([&](auto writer) {
+    writer_ = std::move(writer);
+    return kMonostate;
+  }));
 }
 
 void BufferedFileWriter::WriteNextChunk() {
@@ -79,10 +81,10 @@
 // Handles the result from both WriteFile and WriteChunk. For the former, it is
 // called by OnWriteFileResult after setting writer_.
 void BufferedFileWriter::OnWriteResult(
-    base::Optional<protocol::FileTransfer_Error> error) {
-  if (error) {
+    protocol::FileTransferResult<Monostate> result) {
+  if (!result) {
     SetState(kFailed);
-    std::move(on_error_).Run(std::move(*error));
+    std::move(on_error_).Run(std::move(result.error()));
     return;
   }
 
@@ -103,10 +105,10 @@
 }
 
 void BufferedFileWriter::OnCloseResult(
-    base::Optional<protocol::FileTransfer_Error> error) {
-  if (error) {
+    protocol::FileTransferResult<Monostate> result) {
+  if (!result) {
     SetState(kFailed);
-    std::move(on_error_).Run(std::move(*error));
+    std::move(on_error_).Run(std::move(result.error()));
     return;
   }
 
diff --git a/remoting/host/file_transfer/buffered_file_writer.h b/remoting/host/file_transfer/buffered_file_writer.h
index 62a9949..3ef08c8 100644
--- a/remoting/host/file_transfer/buffered_file_writer.h
+++ b/remoting/host/file_transfer/buffered_file_writer.h
@@ -64,12 +64,13 @@
     kFailed,
   };
 
-  void OnWriteFileResult(base::Optional<protocol::FileTransfer_Error> error,
-                         std::unique_ptr<FileOperations::Writer> writer);
+  void OnWriteFileResult(
+      protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
+          result);
   void WriteNextChunk();
-  void OnWriteResult(base::Optional<protocol::FileTransfer_Error> error);
+  void OnWriteResult(protocol::FileTransferResult<Monostate> result);
   void DoClose();
-  void OnCloseResult(base::Optional<protocol::FileTransfer_Error> error);
+  void OnCloseResult(protocol::FileTransferResult<Monostate> result);
   void SetState(State state);
 
   // Tracks internal state.
diff --git a/remoting/host/file_transfer/ensure_user.h b/remoting/host/file_transfer/ensure_user.h
new file mode 100644
index 0000000..f358e81
--- /dev/null
+++ b/remoting/host/file_transfer/ensure_user.h
@@ -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.
+
+#ifndef REMOTING_HOST_FILE_TRANSFER_ENSURE_USER_H_
+#define REMOTING_HOST_FILE_TRANSFER_ENSURE_USER_H_
+
+#include "remoting/protocol/file_transfer_helpers.h"
+
+namespace remoting {
+
+// Ensures that the given thread is running as a normal user. On macOS and
+// Windows, this ensures a user is logged in. On Windows (where the host runs as
+// SYSTEM), it will additionally call ImpersonateLoggedOnUser to drop privileges
+// on the current thread. As such, it should only be called on a dedicated
+// thread. If success is returned, the thread is running as a normal user. If
+// user is on the log-in screen, an error of type NOT_LOGGED_IN will be
+// returned. If something else goes wrong, the error type will be
+// UNEXPECTED_ERROR.
+protocol::FileTransferResult<Monostate> EnsureUserContext();
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_FILE_TRANSFER_ENSURE_USER_H_
diff --git a/remoting/host/file_transfer/ensure_user_mac.cc b/remoting/host/file_transfer/ensure_user_mac.cc
new file mode 100644
index 0000000..e12f326
--- /dev/null
+++ b/remoting/host/file_transfer/ensure_user_mac.cc
@@ -0,0 +1,21 @@
+// 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 "remoting/host/file_transfer/ensure_user.h"
+
+#include <unistd.h>
+
+namespace remoting {
+
+protocol::FileTransferResult<Monostate> EnsureUserContext() {
+  // Make sure we're not on the log-in screen.
+  if (getuid() == 0) {
+    LOG(ERROR) << "Cannot transfer files on log-in screen.";
+    return protocol::MakeFileTransferError(
+        FROM_HERE, protocol::FileTransfer_Error_Type_NOT_LOGGED_IN);
+  }
+  return kSuccessTag;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/ensure_user_no_op.cc b/remoting/host/file_transfer/ensure_user_no_op.cc
new file mode 100644
index 0000000..4905b97
--- /dev/null
+++ b/remoting/host/file_transfer/ensure_user_no_op.cc
@@ -0,0 +1,13 @@
+// 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 "remoting/host/file_transfer/ensure_user.h"
+
+namespace remoting {
+
+protocol::FileTransferResult<Monostate> EnsureUserContext() {
+  return kSuccessTag;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/ensure_user_win.cc b/remoting/host/file_transfer/ensure_user_win.cc
new file mode 100644
index 0000000..3ecfe197
--- /dev/null
+++ b/remoting/host/file_transfer/ensure_user_win.cc
@@ -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.
+
+#include "remoting/host/file_transfer/ensure_user.h"
+
+#include <Windows.h>
+#include <WtsApi32.h>
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace remoting {
+
+protocol::FileTransferResult<Monostate> EnsureUserContext() {
+  // Impersonate the currently logged-in user, or fail if there is none.
+  HANDLE user_token = nullptr;
+  if (!WTSQueryUserToken(WTS_CURRENT_SESSION, &user_token)) {
+    PLOG(ERROR) << "Failed to get current user token";
+    return protocol::MakeFileTransferError(
+        FROM_HERE,
+        GetLastError() == ERROR_NO_TOKEN
+            ? protocol::FileTransfer_Error_Type_NOT_LOGGED_IN
+            : protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR,
+        GetLastError());
+  }
+  base::win::ScopedHandle scoped_user_token(user_token);
+  if (!ImpersonateLoggedOnUser(scoped_user_token.Get())) {
+    PLOG(ERROR) << "Failed to impersonate user";
+    return protocol::MakeFileTransferError(
+        FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR,
+        GetLastError());
+  }
+  return kSuccessTag;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/fake_file_operations.cc b/remoting/host/file_transfer/fake_file_operations.cc
index f51b1c9..26b442b2 100644
--- a/remoting/host/file_transfer/fake_file_operations.cc
+++ b/remoting/host/file_transfer/fake_file_operations.cc
@@ -51,9 +51,9 @@
                                      WriteFileCallback callback) {
   if (!test_io_->io_error) {
     std::move(callback).Run(
-        base::nullopt, std::make_unique<FakeFileWriter>(test_io_, filename));
+        std::make_unique<FakeFileWriter>(test_io_, filename));
   } else {
-    std::move(callback).Run(test_io_->io_error, nullptr);
+    std::move(callback).Run(*test_io_->io_error);
   }
 }
 
@@ -120,12 +120,12 @@
   if (!test_io_->io_error) {
     chunks_.push_back(std::move(data));
     state_ = kReady;
-    std::move(callback).Run(base::nullopt);
+    std::move(callback).Run(kSuccessTag);
   } else {
     state_ = kFailed;
     test_io_->files_written.push_back(
         OutputFile(filename_, true /* failed */, std::move(chunks_)));
-    std::move(callback).Run(test_io_->io_error);
+    std::move(callback).Run(*test_io_->io_error);
   }
 }
 
@@ -137,12 +137,12 @@
     test_io_->files_written.push_back(
         OutputFile(filename_, false /* failed */, std::move(chunks_)));
     state_ = kClosed;
-    std::move(callback).Run(base::nullopt);
+    std::move(callback).Run(kSuccessTag);
   } else {
     state_ = kFailed;
     test_io_->files_written.push_back(
         OutputFile(filename_, true /* failed */, std::move(chunks_)));
-    std::move(callback).Run(test_io_->io_error);
+    std::move(callback).Run(*test_io_->io_error);
   }
 }
 
diff --git a/remoting/host/file_transfer/file_operations.h b/remoting/host/file_transfer/file_operations.h
index 8d347d8..c778cab 100644
--- a/remoting/host/file_transfer/file_operations.h
+++ b/remoting/host/file_transfer/file_operations.h
@@ -11,7 +11,7 @@
 
 #include "base/callback.h"
 #include "base/optional.h"
-#include "remoting/proto/file_transfer.pb.h"
+#include "remoting/protocol/file_transfer_helpers.h"
 
 namespace base {
 class FilePath;
@@ -41,9 +41,8 @@
 
   class Writer {
    public:
-    // |error| will be nullopt on success or contain error details on failure.
     using Callback = base::OnceCallback<void(
-        base::Optional<protocol::FileTransfer_Error> error)>;
+        protocol::FileTransferResult<Monostate> result)>;
 
     // Destructing FileWriter before calling Close will implicitly call Cancel.
     virtual ~Writer() {}
@@ -62,12 +61,9 @@
 
   class Reader {
    public:
-    // |error| will be nullopt on success or contain error details on failure.
-    // In the event of an error, |data| will contain the data successfully read
-    // before the error, if any.
+    // On success, |result| will contain the read data.
     using Callback = base::OnceCallback<void(
-        base::Optional<protocol::FileTransfer_Error> error,
-        std::string data)>;
+        protocol::FileTransferResult<std::string> result)>;
 
     virtual ~Reader() {}
     // Reads a chunk of the given size from the file.
@@ -76,18 +72,14 @@
     virtual State state() = 0;
   };
 
-  // On success, |error| will be nullopt and |writer| can be used to write data
-  // to the file. On failure, |error| will contain the error details and
-  // |writer| will be null.
+  // On success, |result| will contain a Writer that can be used to write data
+  // to the file.
   using WriteFileCallback = base::OnceCallback<void(
-      base::Optional<protocol::FileTransfer_Error> error,
-      std::unique_ptr<Writer> writer)>;
-  // On success, |error| will be nullopt and |reader| can be used to read data
-  // from the file. On failure, |error| will contain the error details and
-  // |reader| will be null.
+      protocol::FileTransferResult<std::unique_ptr<Writer>> result)>;
+  // On success, |result| will contain a Reader that can be used to read data
+  // from the file.
   using ReadFileCallback = base::OnceCallback<void(
-      base::Optional<protocol::FileTransfer_Error> error,
-      std::unique_ptr<Reader> reader)>;
+      protocol::FileTransferResult<std::unique_ptr<Reader>> result)>;
 
   virtual ~FileOperations() {}
 
diff --git a/remoting/host/file_transfer/file_transfer_message_handler.cc b/remoting/host/file_transfer/file_transfer_message_handler.cc
index a4fd02f..5caeb36 100644
--- a/remoting/host/file_transfer/file_transfer_message_handler.cc
+++ b/remoting/host/file_transfer/file_transfer_message_handler.cc
@@ -141,7 +141,7 @@
 }
 
 void FileTransferMessageHandler::OnComplete() {
-  SendResult(base::nullopt);  // Success
+  SendResult(kSuccessTag);  // Success
 }
 
 void FileTransferMessageHandler::OnError(protocol::FileTransfer_Error error) {
@@ -149,12 +149,12 @@
 }
 
 void FileTransferMessageHandler::SendResult(
-    base::Optional<protocol::FileTransfer_Error> error) {
+    protocol::FileTransferResult<Monostate> result) {
   protocol::FileTransfer result_message;
-  if (error) {
-    *result_message.mutable_error() = std::move(*error);
-  } else {
+  if (result) {
     result_message.mutable_success();
+  } else {
+    *result_message.mutable_error() = std::move(result.error());
   }
   Send(result_message, base::Closure());
 }
diff --git a/remoting/host/file_transfer/file_transfer_message_handler.h b/remoting/host/file_transfer/file_transfer_message_handler.h
index 9a8139be..0b735ca 100644
--- a/remoting/host/file_transfer/file_transfer_message_handler.h
+++ b/remoting/host/file_transfer/file_transfer_message_handler.h
@@ -14,7 +14,7 @@
 #include "base/optional.h"
 #include "remoting/host/file_transfer/buffered_file_writer.h"
 #include "remoting/host/file_transfer/file_operations.h"
-#include "remoting/proto/file_transfer.pb.h"
+#include "remoting/protocol/file_transfer_helpers.h"
 #include "remoting/protocol/named_message_pipe_handler.h"
 
 namespace remoting {
@@ -51,7 +51,7 @@
   void Cancel();
   void OnComplete();
   void OnError(protocol::FileTransfer_Error error);
-  void SendResult(base::Optional<protocol::FileTransfer_Error> error);
+  void SendResult(protocol::FileTransferResult<Monostate> result);
   void CancelAndSendError(protocol::FileTransfer_Error error);
   void SetState(State state);
 
diff --git a/remoting/host/file_transfer/get_desktop_directory.cc b/remoting/host/file_transfer/get_desktop_directory.cc
new file mode 100644
index 0000000..a4b18d0e
--- /dev/null
+++ b/remoting/host/file_transfer/get_desktop_directory.cc
@@ -0,0 +1,21 @@
+// 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 "remoting/host/file_transfer/get_desktop_directory.h"
+
+#include "base/path_service.h"
+
+namespace remoting {
+
+protocol::FileTransferResult<base::FilePath> GetDesktopDirectory() {
+  base::FilePath target_directory;
+  if (!base::PathService::Get(base::DIR_USER_DESKTOP, &target_directory)) {
+    LOG(ERROR) << "Failed to get DIR_USER_DESKTOP from base::PathService::Get";
+    return protocol::MakeFileTransferError(
+        FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR);
+  }
+  return target_directory;
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/get_desktop_directory.h b/remoting/host/file_transfer/get_desktop_directory.h
new file mode 100644
index 0000000..6d94261
--- /dev/null
+++ b/remoting/host/file_transfer/get_desktop_directory.h
@@ -0,0 +1,20 @@
+// 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 REMOTING_HOST_FILE_TRANSFER_GET_DESKTOP_DIRECTORY_H_
+#define REMOTING_HOST_FILE_TRANSFER_GET_DESKTOP_DIRECTORY_H_
+
+#include "base/files/file_path.h"
+#include "remoting/protocol/file_transfer_helpers.h"
+
+namespace remoting {
+
+// Retrieves the path to the user's desktop folder. This should be run in the
+// context of the user, which on Windows means it must be run on the same
+// dedicated thread on which EnsureUser was called.
+protocol::FileTransferResult<base::FilePath> GetDesktopDirectory();
+
+}  // namespace remoting
+
+#endif  // REMOTING_HOST_FILE_TRANSFER_GET_DESKTOP_DIRECTORY_H_
diff --git a/remoting/host/file_transfer/get_desktop_directory_win.cc b/remoting/host/file_transfer/get_desktop_directory_win.cc
new file mode 100644
index 0000000..9d7d0d65
--- /dev/null
+++ b/remoting/host/file_transfer/get_desktop_directory_win.cc
@@ -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.
+
+#include "remoting/host/file_transfer/get_desktop_directory.h"
+
+#include <Windows.h>
+#include <shlobj.h>
+
+namespace remoting {
+
+// We can't use PathService on Windows because it doesn't play nicely with
+// impersonation. Even if we disable PathService's own cache, the Windows API
+// itself does process-wide caching that can cause trouble on an impersonating
+// thread. As such, we have to call the relevant API directly and be explicit
+// about wanting impersonation handling.
+protocol::FileTransferResult<base::FilePath> GetDesktopDirectory() {
+  wchar_t buffer[MAX_PATH];
+  buffer[0] = 0;
+  // While passing NULL for the third parameter would normally get the directory
+  // for the current user, there are process-wide caches that can cause trouble
+  // when impersonation is in play, so specify the token explicitly.
+  HRESULT hr =
+      SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, GetCurrentThreadToken(),
+                      SHGFP_TYPE_CURRENT, buffer);
+  if (FAILED(hr)) {
+    LOG(ERROR) << "Failed to get desktop directory: " << hr;
+    return protocol::MakeFileTransferError(
+        FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR, hr);
+  }
+
+  return {kSuccessTag, buffer};
+}
+
+}  // namespace remoting
diff --git a/remoting/host/file_transfer/ipc_file_operations.cc b/remoting/host/file_transfer/ipc_file_operations.cc
index aadfaaa..c23ca1a 100644
--- a/remoting/host/file_transfer/ipc_file_operations.cc
+++ b/remoting/host/file_transfer/ipc_file_operations.cc
@@ -14,9 +14,6 @@
 
 namespace remoting {
 
-using ResultCallback =
-    base::OnceCallback<void(base::Optional<protocol::FileTransfer_Error>)>;
-
 class IpcFileOperations::IpcWriter : public FileOperations::Writer {
  public:
   IpcWriter(std::uint64_t file_id, base::WeakPtr<SharedState> shared_state);
@@ -30,9 +27,9 @@
 
  private:
   void OnWriteChunkResult(Callback callback,
-                          base::Optional<protocol::FileTransfer_Error> error);
+                          protocol::FileTransferResult<Monostate> result);
   void OnCloseResult(Callback callback,
-                     base::Optional<protocol::FileTransfer_Error> error);
+                     protocol::FileTransferResult<Monostate> result);
 
   State state_ = kReady;
   std::uint64_t file_id_;
@@ -72,13 +69,9 @@
 void IpcFileOperations::OnWriteFileResult(
     std::unique_ptr<IpcWriter> writer,
     WriteFileCallback callback,
-    base::Optional<protocol::FileTransfer_Error> error) {
-  if (error) {
-    // If there's an error, writer should be null.
-    writer.reset();
-  }
-
-  std::move(callback).Run(std::move(error), std::move(writer));
+    protocol::FileTransferResult<Monostate> result) {
+  std::move(callback).Run(
+      std::move(result).Map([&](Monostate) { return std::move(writer); }));
 }
 
 IpcFileOperationsFactory::IpcFileOperationsFactory(
@@ -95,12 +88,13 @@
 
 void IpcFileOperationsFactory::OnResult(
     uint64_t file_id,
-    base::Optional<protocol::FileTransfer_Error> error) {
+    protocol::FileTransferResult<Monostate> result) {
   auto callback_iter = shared_state_.result_callbacks.find(file_id);
   if (callback_iter != shared_state_.result_callbacks.end()) {
-    ResultCallback callback = std::move(callback_iter->second);
+    IpcFileOperations::ResultCallback callback =
+        std::move(callback_iter->second);
     shared_state_.result_callbacks.erase(callback_iter);
-    std::move(callback).Run(error);
+    std::move(callback).Run(std::move(result));
   }
 }
 
@@ -166,24 +160,24 @@
 
 void IpcFileOperations::IpcWriter::OnWriteChunkResult(
     Callback callback,
-    base::Optional<protocol::FileTransfer_Error> error) {
-  if (error) {
-    state_ = kFailed;
-  } else {
+    protocol::FileTransferResult<Monostate> result) {
+  if (result) {
     state_ = kReady;
+  } else {
+    state_ = kFailed;
   }
-  std::move(callback).Run(error);
+  std::move(callback).Run(std::move(result));
 }
 
 void IpcFileOperations::IpcWriter::OnCloseResult(
     Callback callback,
-    base::Optional<protocol::FileTransfer_Error> error) {
-  if (error) {
-    state_ = kFailed;
-  } else {
+    protocol::FileTransferResult<Monostate> result) {
+  if (result) {
     state_ = kClosed;
+  } else {
+    state_ = kFailed;
   }
-  std::move(callback).Run(error);
+  std::move(callback).Run(std::move(result));
 }
 
 }  // namespace remoting
diff --git a/remoting/host/file_transfer/ipc_file_operations.h b/remoting/host/file_transfer/ipc_file_operations.h
index e2dec76..dbad2e7a 100644
--- a/remoting/host/file_transfer/ipc_file_operations.h
+++ b/remoting/host/file_transfer/ipc_file_operations.h
@@ -11,7 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "remoting/host/file_transfer/file_operations.h"
-#include "remoting/proto/file_transfer.pb.h"
+#include "remoting/protocol/file_transfer_helpers.h"
 
 namespace remoting {
 
@@ -39,9 +39,8 @@
   class ResultHandler {
    public:
     virtual ~ResultHandler() = default;
-    virtual void OnResult(
-        std::uint64_t file_id,
-        base::Optional<protocol::FileTransfer_Error> error) = 0;
+    virtual void OnResult(std::uint64_t file_id,
+                          protocol::FileTransferResult<Monostate> result) = 0;
   };
 
   ~IpcFileOperations() override;
@@ -53,7 +52,7 @@
 
  private:
   using ResultCallback =
-      base::OnceCallback<void(base::Optional<protocol::FileTransfer_Error>)>;
+      base::OnceCallback<void(protocol::FileTransferResult<Monostate>)>;
 
   class IpcWriter;
 
@@ -80,10 +79,9 @@
 
   explicit IpcFileOperations(base::WeakPtr<SharedState> shared_state);
 
-  static void OnWriteFileResult(
-      std::unique_ptr<IpcWriter> writer,
-      WriteFileCallback callback,
-      base::Optional<protocol::FileTransfer_Error> error);
+  static void OnWriteFileResult(std::unique_ptr<IpcWriter> writer,
+                                WriteFileCallback callback,
+                                protocol::FileTransferResult<Monostate> result);
 
   // Contains shared state used by all instances tied to a given
   // RequestHandler.
@@ -108,7 +106,7 @@
 
   // ResultHandler implementation.
   void OnResult(std::uint64_t file_id,
-                base::Optional<protocol::FileTransfer_Error> error) override;
+                protocol::FileTransferResult<Monostate> result) override;
 
  private:
   IpcFileOperations::SharedState shared_state_;
diff --git a/remoting/host/file_transfer/local_file_operations.cc b/remoting/host/file_transfer/local_file_operations.cc
index 77639032..f40b08c 100644
--- a/remoting/host/file_transfer/local_file_operations.cc
+++ b/remoting/host/file_transfer/local_file_operations.cc
@@ -18,6 +18,10 @@
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "build/build_config.h"
+#include "remoting/base/result.h"
+#include "remoting/host/file_transfer/ensure_user.h"
+#include "remoting/host/file_transfer/get_desktop_directory.h"
 #include "remoting/protocol/file_transfer_helpers.h"
 
 namespace remoting {
@@ -56,8 +60,13 @@
                   std::unique_ptr<base::FileProxy> file_proxy,
                   const base::FilePath& destination_filepath);
 
-  // Callbacks for CreateFile(). These are static because they're used in the
-  // construction of TheadedFileWriter.
+  // Callbacks for WriteFile(). These are static because they're used in the
+  // construction of LocalFileWriter.
+  static void OnGetTargetDirectoryResult(
+      scoped_refptr<base::SequencedTaskRunner> file_task_runner,
+      base::FilePath filename,
+      FileOperations::WriteFileCallback callback,
+      protocol::FileTransferResult<base::FilePath> target_directory_result);
   static void CreateTempFile(std::unique_ptr<LocalFileWriter> writer,
                              FileOperations::WriteFileCallback callback,
                              int unique_path_number);
@@ -156,26 +165,44 @@
 
 void LocalFileWriter::WriteFile(const base::FilePath& filename,
                                 FileOperations::WriteFileCallback callback) {
-  base::FilePath target_directory;
-  if (!base::PathService::Get(base::DIR_USER_DESKTOP, &target_directory)) {
-    LOG(ERROR) << "Failed to get DIR_USER_DESKTOP from base::PathService::Get";
-
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            std::move(callback),
-            protocol::MakeFileTransferError(
-                FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR),
-            nullptr));
-    return;
-  }
-
+#if defined(OS_WIN)
+  // On Windows, we use user impersonation to write files as the currently
+  // logged-in user, while the process as a whole runs as SYSTEM. Since user
+  // impersonation is per-thread on Windows, we need a dedicated thread to
+  // ensure that no other code is accidentally run with the wrong privileges.
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner =
+      base::CreateSingleThreadTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
+          base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+#else
   scoped_refptr<base::SequencedTaskRunner> file_task_runner =
       base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
+#endif
+  base::PostTaskAndReplyWithResult(
+      file_task_runner.get(), FROM_HERE, base::BindOnce([] {
+        return EnsureUserContext().AndThen(
+            [](Monostate) { return GetDesktopDirectory(); });
+      }),
+      base::BindOnce(&OnGetTargetDirectoryResult, file_task_runner, filename,
+                     std::move(callback)));
+}
+
+void LocalFileWriter::OnGetTargetDirectoryResult(
+    scoped_refptr<base::SequencedTaskRunner> file_task_runner,
+    base::FilePath filename,
+    FileOperations::WriteFileCallback callback,
+    protocol::FileTransferResult<base::FilePath> target_directory_result) {
   DCHECK(file_task_runner);
+  if (!target_directory_result) {
+    std::move(callback).Run(std::move(target_directory_result.error()));
+    return;
+  }
+
+  base::FilePath target_directory =
+      std::move(target_directory_result.success());
   base::SequencedTaskRunner* file_task_runner_ptr = file_task_runner.get();
-  auto file_proxy = std::make_unique<base::FileProxy>(file_task_runner.get());
+  auto file_proxy = std::make_unique<base::FileProxy>(file_task_runner_ptr);
   base::FilePath destination_filepath =
       target_directory.Append(filename.BaseName());
 
@@ -222,10 +249,8 @@
                                      base::File::Error error) {
   if (error != base::File::FILE_OK) {
     LOG(ERROR) << "Creating temp file failed with error: " << error;
-    std::move(callback).Run(
-        protocol::MakeFileTransferError(
-            FROM_HERE, FileErrorToResponseErrorType(error), error),
-        nullptr);
+    std::move(callback).Run(protocol::MakeFileTransferError(
+        FROM_HERE, FileErrorToResponseErrorType(error), error));
   } else {
     // Now that the temp file has been created successfully, we could lock it
     // using base::File::Lock(), but this would not prevent the file from being
@@ -233,7 +258,7 @@
     // the file as if the file was still there, and an error will occur when
     // calling base::Move() to move the temp file. Chrome exhibits the same
     // behavior with its downloads.
-    std::move(callback).Run(base::nullopt, std::move(writer));
+    std::move(callback).Run(std::move(writer));
   }
 }
 
@@ -262,7 +287,7 @@
     return;
   }
 
-  std::move(callback).Run(base::nullopt);
+  std::move(callback).Run(kSuccessTag);
 }
 
 void LocalFileWriter::OnCloseResult(Callback callback,
@@ -299,7 +324,7 @@
 void LocalFileWriter::OnMoveResult(Callback callback, bool success) {
   if (success) {
     SetState(FileOperations::kClosed);
-    std::move(callback).Run(base::nullopt);
+    std::move(callback).Run(kSuccessTag);
   } else {
     LOG(ERROR) << "Failed to move file to final destination.";
     Cancel();
diff --git a/remoting/host/file_transfer/local_file_operations_unittest.cc b/remoting/host/file_transfer/local_file_operations_unittest.cc
index 43c5c8e9..73bf2bff 100644
--- a/remoting/host/file_transfer/local_file_operations_unittest.cc
+++ b/remoting/host/file_transfer/local_file_operations_unittest.cc
@@ -36,14 +36,15 @@
   void WriteFile(const base::FilePath& filename,
                  base::queue<std::string> chunks,
                  bool close);
-  void OnFileCreated(base::queue<std::string> chunks,
-                     bool close,
-                     base::Optional<protocol::FileTransfer_Error> error,
-                     std::unique_ptr<FileOperations::Writer> writer);
+  void OnFileCreated(
+      base::queue<std::string> chunks,
+      bool close,
+      protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
+          result);
   void OnWriteComplete(base::queue<std::string> remaining_chunks,
                        bool close,
-                       base::Optional<protocol::FileTransfer_Error> error);
-  void OnCloseComplete(base::Optional<protocol::FileTransfer_Error> error);
+                       protocol::FileTransferResult<Monostate> result);
+  void OnCloseComplete(protocol::FileTransferResult<Monostate> result);
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::ScopedPathOverride scoped_path_override_;
@@ -83,17 +84,20 @@
 void LocalFileOperationsTest::OnFileCreated(
     base::queue<std::string> chunks,
     bool close,
-    base::Optional<protocol::FileTransfer_Error> error,
-    std::unique_ptr<FileOperations::Writer> writer) {
-  file_writer_ = std::move(writer);
-  OnWriteComplete(std::move(chunks), close, error);
+    protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
+        result) {
+  OnWriteComplete(std::move(chunks), close,
+                  std::move(result).Map([&](auto writer) {
+                    file_writer_ = std::move(writer);
+                    return kMonostate;
+                  }));
 }
 
 void LocalFileOperationsTest::OnWriteComplete(
     base::queue<std::string> remaining_chunks,
     bool close,
-    base::Optional<protocol::FileTransfer_Error> error) {
-  ASSERT_FALSE(error);
+    protocol::FileTransferResult<Monostate> result) {
+  ASSERT_TRUE(result);
   if (!remaining_chunks.empty()) {
     std::string next_chunk = std::move(remaining_chunks.front());
     remaining_chunks.pop();
@@ -111,8 +115,8 @@
 }
 
 void LocalFileOperationsTest::OnCloseComplete(
-    base::Optional<protocol::FileTransfer_Error> error) {
-  ASSERT_FALSE(error);
+    protocol::FileTransferResult<Monostate> result) {
+  ASSERT_TRUE(result);
   operation_completed_ = true;
 }
 
diff --git a/remoting/host/file_transfer/session_file_operations_handler.cc b/remoting/host/file_transfer/session_file_operations_handler.cc
index 1937551..47d9077 100644
--- a/remoting/host/file_transfer/session_file_operations_handler.cc
+++ b/remoting/host/file_transfer/session_file_operations_handler.cc
@@ -4,7 +4,6 @@
 
 #include "remoting/host/file_transfer/session_file_operations_handler.h"
 #include "base/bind.h"
-#include "remoting/host/desktop_session_agent.h"
 #include "remoting/protocol/file_transfer_helpers.h"
 
 namespace remoting {
@@ -67,28 +66,40 @@
 
 void SessionFileOperationsHandler::OnWriteFileResult(
     uint64_t file_id,
-    base::Optional<protocol::FileTransfer_Error> error,
-    std::unique_ptr<FileOperations::Writer> writer) {
-  if (!error) {
-    writers_.emplace(file_id, std::move(writer));
+    protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
+        result) {
+  if (!result_handler_) {
+    return;
   }
-  result_handler_->OnResult(file_id, error);
+
+  result_handler_->OnResult(file_id, std::move(result).Map([&](auto writer) {
+    writers_.emplace(file_id, std::move(writer));
+    return kMonostate;
+  }));
 }
 
 void SessionFileOperationsHandler::OnWriteChunkResult(
     uint64_t file_id,
-    base::Optional<protocol::FileTransfer_Error> error) {
-  if (error) {
+    protocol::FileTransferResult<Monostate> result) {
+  if (!result_handler_) {
+    return;
+  }
+
+  if (!result) {
     writers_.erase(file_id);
   }
-  result_handler_->OnResult(file_id, error);
+  result_handler_->OnResult(file_id, std::move(result));
 }
 
 void SessionFileOperationsHandler::OnCloseResult(
     uint64_t file_id,
-    base::Optional<protocol::FileTransfer_Error> error) {
+    protocol::FileTransferResult<Monostate> result) {
+  if (!result_handler_) {
+    return;
+  }
+
   writers_.erase(file_id);
-  result_handler_->OnResult(file_id, error);
+  result_handler_->OnResult(file_id, std::move(result));
 }
 
 SessionFileOperationsHandler::~SessionFileOperationsHandler() = default;
diff --git a/remoting/host/file_transfer/session_file_operations_handler.h b/remoting/host/file_transfer/session_file_operations_handler.h
index a2422d9..d8a1610 100644
--- a/remoting/host/file_transfer/session_file_operations_handler.h
+++ b/remoting/host/file_transfer/session_file_operations_handler.h
@@ -33,13 +33,14 @@
   void Cancel(std::uint64_t file_id) override;
 
  private:
-  void OnWriteFileResult(std::uint64_t file_id,
-                         base::Optional<protocol::FileTransfer_Error> error,
-                         std::unique_ptr<FileOperations::Writer> writer);
+  void OnWriteFileResult(
+      std::uint64_t file_id,
+      protocol::FileTransferResult<std::unique_ptr<FileOperations::Writer>>
+          result);
   void OnWriteChunkResult(std::uint64_t file_id,
-                          base::Optional<protocol::FileTransfer_Error> error);
+                          protocol::FileTransferResult<Monostate> result);
   void OnCloseResult(std::uint64_t file_id,
-                     base::Optional<protocol::FileTransfer_Error> error);
+                     protocol::FileTransferResult<Monostate> result);
 
   IpcFileOperations::ResultHandler* result_handler_;
   std::unique_ptr<FileOperations> file_operations_;
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 1d8b50d..39dc4673 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -3,8 +3,8 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
-import("//remoting/remoting_options.gni")
 import("//remoting/remoting_locales.gni")
+import("//remoting/remoting_options.gni")
 import("//remoting/remoting_version.gni")
 
 if (is_win) {
@@ -48,7 +48,7 @@
     "//mojo/core/embedder",
     "//net",
     "//remoting/base",
-    "//remoting/host",
+    "//remoting/host:common",
     "//remoting/protocol",
     "//remoting/resources",
     "//remoting/signaling",
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index fa24d0b9..a07f8b0 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -172,12 +172,12 @@
   configs += [ "//remoting/build/config:version" ]
 
   deps = [
-    "//remoting/host",
+    "//remoting/host:common",
     "//remoting/host:test_support",
     "//remoting/host/it2me:common",
     "//remoting/host/native_messaging",
     "//remoting/host/security_key:unit_tests",
-    "//remoting/host/setup",
+    "//remoting/host/setup:common",
     "//remoting/proto",
     "//remoting/resources",
     "//skia",
diff --git a/remoting/host/setup/BUILD.gn b/remoting/host/setup/BUILD.gn
index 3f6cef8..5b5f4d13 100644
--- a/remoting/host/setup/BUILD.gn
+++ b/remoting/host/setup/BUILD.gn
@@ -3,6 +3,15 @@
 # found in the LICENSE file.
 
 source_set("setup") {
+  public_deps = [
+    ":common",
+  ]
+  deps = [
+    "//remoting/host",
+  ]
+}
+
+source_set("common") {
   sources = [
     "daemon_controller.cc",
     "daemon_controller.h",
@@ -36,7 +45,7 @@
     "//google_apis",
     "//mojo/core/embedder",
     "//remoting/base:authorization",
-    "//remoting/host",
+    "//remoting/host:common",
     "//remoting/host/native_messaging",
     "//services/network/public/cpp",
     "//services/network/public/mojom",
diff --git a/remoting/proto/file_transfer.proto b/remoting/proto/file_transfer.proto
index 9f7304d..e67d597e 100644
--- a/remoting/proto/file_transfer.proto
+++ b/remoting/proto/file_transfer.proto
@@ -53,6 +53,7 @@
       PERMISSION_DENIED = 3;
       OUT_OF_DISK_SPACE = 4;
       IO_ERROR = 5;
+      NOT_LOGGED_IN = 6;
     }
 
     // An error category to be used to select a user-displayed error message.
diff --git a/remoting/protocol/file_transfer_helpers.h b/remoting/protocol/file_transfer_helpers.h
index 81edc9d..1fc38e77 100644
--- a/remoting/protocol/file_transfer_helpers.h
+++ b/remoting/protocol/file_transfer_helpers.h
@@ -10,11 +10,15 @@
 
 #include "base/location.h"
 #include "base/optional.h"
+#include "remoting/base/result.h"
 #include "remoting/proto/file_transfer.pb.h"
 
 namespace remoting {
 namespace protocol {
 
+template <typename SuccessType>
+using FileTransferResult = Result<SuccessType, FileTransfer_Error>;
+
 FileTransfer_Error MakeFileTransferError(
     base::Location location,
     FileTransfer_Error_Type type,
diff --git a/remoting/test/BUILD.gn b/remoting/test/BUILD.gn
index 2670491..0bb66b9b 100644
--- a/remoting/test/BUILD.gn
+++ b/remoting/test/BUILD.gn
@@ -159,7 +159,7 @@
     ]
 
     deps = [
-      "//remoting/host",
+      "//remoting/host:common",
       "//remoting/protocol:test_support",
     ]
   }
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index f1d341b6..bd791b6 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -26,7 +26,8 @@
     mojom::URLLoaderFactoryRequest request,
     const OriginAccessList* origin_access_list)
     : context_(context),
-      disable_web_security_(params && params->disable_web_security),
+      disable_web_security_(params->disable_web_security),
+      process_id_(params->process_id),
       network_loader_factory_(std::make_unique<network::URLLoaderFactory>(
           context,
           std::move(params),
@@ -45,8 +46,10 @@
     bool disable_web_security,
     std::unique_ptr<mojom::URLLoaderFactory> network_loader_factory,
     const base::RepeatingCallback<void(int)>& preflight_finalizer,
-    const OriginAccessList* origin_access_list)
+    const OriginAccessList* origin_access_list,
+    uint32_t process_id)
     : disable_web_security_(disable_web_security),
+      process_id_(process_id),
       network_loader_factory_(std::move(network_loader_factory)),
       preflight_finalizer_(preflight_finalizer),
       origin_access_list_(origin_access_list) {
@@ -61,10 +64,14 @@
 
 void CorsURLLoaderFactory::OnLoaderCreated(
     std::unique_ptr<mojom::URLLoader> loader) {
+  if (context_)
+    context_->LoaderCreated(process_id_);
   loaders_.insert(std::move(loader));
 }
 
 void CorsURLLoaderFactory::DestroyURLLoader(mojom::URLLoader* loader) {
+  if (context_)
+    context_->LoaderDestroyed(process_id_);
   auto it = loaders_.find(loader);
   DCHECK(it != loaders_.end());
   loaders_.erase(it);
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h
index 41aa48e..a79eb7f 100644
--- a/services/network/cors/cors_url_loader_factory.h
+++ b/services/network/cors/cors_url_loader_factory.h
@@ -47,7 +47,8 @@
       bool disable_web_security,
       std::unique_ptr<mojom::URLLoaderFactory> network_loader_factory,
       const base::RepeatingCallback<void(int)>& preflight_finalizer,
-      const OriginAccessList* origin_access_list);
+      const OriginAccessList* origin_access_list,
+      uint32_t process_id);
   ~CorsURLLoaderFactory() override;
 
   void OnLoaderCreated(std::unique_ptr<mojom::URLLoader> loader);
@@ -82,6 +83,8 @@
 
   const bool disable_web_security_;
 
+  const uint32_t process_id_;
+
   // Relative order of |network_loader_factory_| and |loaders_| matters -
   // URLLoaderFactory needs to live longer than URLLoaders created using the
   // factory.  See also https://crbug.com/906305.
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
index 7eac462..be4cfcd 100644
--- a/services/network/cors/cors_url_loader_unittest.cc
+++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -124,7 +124,7 @@
     test_url_loader_factory_ = factory->GetWeakPtr();
     cors_url_loader_factory_ = std::make_unique<CorsURLLoaderFactory>(
         false, std::move(factory), base::RepeatingCallback<void(int)>(),
-        &origin_access_list_);
+        &origin_access_list_, 0);
   }
 
  protected:
diff --git a/services/network/mdns_responder.cc b/services/network/mdns_responder.cc
index ff572cb..a3aadf77 100644
--- a/services/network/mdns_responder.cc
+++ b/services/network/mdns_responder.cc
@@ -807,6 +807,13 @@
 void MdnsResponder::CreateNameForAddress(
     const net::IPAddress& address,
     mojom::MdnsResponder::CreateNameForAddressCallback callback) {
+  DCHECK(address.IsValid() || address.empty());
+  if (!address.IsValid()) {
+    LOG(ERROR) << "Invalid IP address to create a name for";
+    binding_.Close();
+    manager_->OnMojoConnectionError(this);
+    return;
+  }
   std::string name;
   auto it = FindNameCreatedForAddress(address);
   bool announcement_sched_at_least_once = false;
@@ -847,6 +854,7 @@
 void MdnsResponder::RemoveNameForAddress(
     const net::IPAddress& address,
     mojom::MdnsResponder::RemoveNameForAddressCallback callback) {
+  DCHECK(address.IsValid() || address.empty());
   auto it = FindNameCreatedForAddress(address);
   if (it == name_addr_map_.end()) {
     std::move(callback).Run(false /* removed */, false /* goodbye_scheduled */);
diff --git a/services/network/mdns_responder_unittest.cc b/services/network/mdns_responder_unittest.cc
index db428cb..f77370c 100644
--- a/services/network/mdns_responder_unittest.cc
+++ b/services/network/mdns_responder_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_piece.h"
 #include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/connector.h"
 #include "net/base/ip_address.h"
 #include "net/dns/dns_query.h"
 #include "net/dns/dns_response.h"
@@ -536,6 +537,19 @@
   }
 }
 
+// Test that the responder manager closes the connection after
+// an invalid IP address is given to create a name for.
+TEST_F(MdnsResponderTest,
+       HostClosesMojoConnectionWhenCreatingNameForInvalidAddress) {
+  const net::IPAddress addr;
+  ASSERT_TRUE(!addr.IsValid());
+  EXPECT_TRUE(client_[0].is_bound());
+  // No packet should be sent out from interfaces.
+  EXPECT_CALL(socket_factory_, OnSendTo(_)).Times(0);
+  CreateNameForAddress(0, addr);
+  EXPECT_FALSE(client_[0].is_bound());
+}
+
 // Test that the responder manager closes the connection after observing
 // conflicting name resolution in the network.
 TEST_F(MdnsResponderTest, HostClosesMojoConnectionAfterObservingNameConflict) {
@@ -553,7 +567,11 @@
   std::string expected_goodbye = CreateResolutionResponse(
       base::TimeDelta(), {{name1, addr1}, {name2, addr2}});
 
+  EXPECT_TRUE(client_[0].is_bound());
   // MockMdnsSocketFactory binds sockets to two interfaces.
+  // We should send only two goodbyes before closing the connection and no
+  // packet should be sent out from interfaces after the connection is closed.
+  EXPECT_CALL(socket_factory_, OnSendTo(_)).Times(0);
   EXPECT_CALL(socket_factory_, OnSendTo(expected_goodbye)).Times(2);
   socket_factory_.SimulateReceive(
       reinterpret_cast<const uint8_t*>(conflicting_response.data()),
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 7a3754d..02d80787 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -322,6 +322,7 @@
 
 }  // namespace
 
+constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess;
 constexpr bool NetworkContext::enable_resource_scheduler_;
 
 NetworkContext::PendingCertVerify::PendingCertVerify() = default;
@@ -737,6 +738,24 @@
   url_loader_factories_.erase(it);
 }
 
+void NetworkContext::LoaderCreated(uint32_t process_id) {
+  loader_count_per_process_[process_id] += 1;
+}
+
+void NetworkContext::LoaderDestroyed(uint32_t process_id) {
+  auto it = loader_count_per_process_.find(process_id);
+  DCHECK(it != loader_count_per_process_.end());
+  it->second -= 1;
+  if (it->second == 0)
+    loader_count_per_process_.erase(it);
+}
+
+bool NetworkContext::CanCreateLoader(uint32_t process_id) {
+  auto it = loader_count_per_process_.find(process_id);
+  uint32_t count = (it == loader_count_per_process_.end() ? 0 : it->second);
+  return count < max_loaders_per_process_;
+}
+
 size_t NetworkContext::GetNumOutstandingResolveHostRequestsForTesting() const {
   size_t sum = 0;
   if (internal_host_resolver_)
diff --git a/services/network/network_context.h b/services/network/network_context.h
index c784de05..f435890 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -349,6 +349,16 @@
   // no open pipes.
   void DestroyURLLoaderFactory(cors::CorsURLLoaderFactory* url_loader_factory);
 
+  // The following methods are used to track the number of requests per process
+  // and ensure it doesn't go over a reasonable limit.
+  void LoaderCreated(uint32_t process_id);
+  void LoaderDestroyed(uint32_t process_id);
+  bool CanCreateLoader(uint32_t process_id);
+
+  void set_max_loaders_per_process_for_testing(uint32_t count) {
+    max_loaders_per_process_ = count;
+  }
+
   size_t GetNumOutstandingResolveHostRequestsForTesting() const;
 
   size_t pending_proxy_lookup_requests_for_testing() const {
@@ -480,6 +490,12 @@
            base::UniquePtrComparator>
       url_loader_factories_;
 
+  // A count of outstanding requests per initiating process.
+  std::map<uint32_t, uint32_t> loader_count_per_process_;
+
+  static constexpr uint32_t kMaxOutstandingRequestsPerProcess = 2700;
+  uint32_t max_loaders_per_process_ = kMaxOutstandingRequestsPerProcess;
+
   base::flat_map<P2PSocketManager*, std::unique_ptr<P2PSocketManager>>
       socket_managers_;
 
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index e0c9603..6d91b1b 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -4758,6 +4758,80 @@
             ConvertToProxyServer(proxy_test_server));
 }
 
+TEST_F(NetworkContextTest, MaximumCount) {
+  net::EmbeddedTestServer test_server;
+  test_server.AddDefaultHandlers(
+      base::FilePath(FILE_PATH_LITERAL("services/test/data")));
+
+  const char kPath1[] = "/foobar";
+  const char kPath2[] = "/hung";
+  const char kPath3[] = "/hello.html";
+  net::test_server::ControllableHttpResponse controllable_response1(
+      &test_server, kPath1);
+
+  ASSERT_TRUE(test_server.Start());
+
+  std::unique_ptr<NetworkContext> network_context =
+      CreateContextWithParams(CreateContextParams());
+  network_context->set_max_loaders_per_process_for_testing(2);
+
+  mojom::URLLoaderFactoryPtr loader_factory;
+  mojom::URLLoaderFactoryParamsPtr params =
+      mojom::URLLoaderFactoryParams::New();
+  params->process_id = mojom::kBrowserProcessId;
+  params->is_corb_enabled = false;
+  network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
+                                          std::move(params));
+
+  ResourceRequest request;
+  request.url = test_server.GetURL(kPath1);
+  auto client1 = std::make_unique<TestURLLoaderClient>();
+  mojom::URLLoaderPtr loader1;
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&loader1), 0 /* routing_id */, 0 /* request_id */,
+      0 /* options */, request, client1->CreateInterfacePtr(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  request.url = test_server.GetURL(kPath2);
+  auto client2 = std::make_unique<TestURLLoaderClient>();
+  mojom::URLLoaderPtr loader2;
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&loader2), 0 /* routing_id */, 0 /* request_id */,
+      0 /* options */, request, client2->CreateInterfacePtr(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  // A third request should fail, since the first two are outstanding and the
+  // limit is 2.
+  request.url = test_server.GetURL(kPath3);
+  auto client3 = std::make_unique<TestURLLoaderClient>();
+  mojom::URLLoaderPtr loader3;
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&loader3), 0 /* routing_id */, 0 /* request_id */,
+      0 /* options */, request, client3->CreateInterfacePtr(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  client3->RunUntilComplete();
+  ASSERT_EQ(client3->completion_status().error_code,
+            net::ERR_INSUFFICIENT_RESOURCES);
+
+  // Complete the first request and try the third again.
+  controllable_response1.WaitForRequest();
+  controllable_response1.Send("HTTP/1.1 200 OK\r\n");
+  controllable_response1.Done();
+
+  client1->RunUntilComplete();
+  ASSERT_EQ(client1->completion_status().error_code, net::OK);
+
+  client3 = std::make_unique<TestURLLoaderClient>();
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&loader3), 0 /* routing_id */, 0 /* request_id */,
+      0 /* options */, request, client3->CreateInterfacePtr(),
+      net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  client3->RunUntilComplete();
+  ASSERT_EQ(client3->completion_status().error_code, net::OK);
+}
+
 }  // namespace
 
 }  // namespace network
diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc
index 0b93d46..669890a 100644
--- a/services/network/url_loader_factory.cc
+++ b/services/network/url_loader_factory.cc
@@ -95,10 +95,10 @@
         context_->network_service()->network_usage_accumulator()->AsWeakPtr();
   }
 
+  bool exhausted = false;
   if (url_request.keepalive && keepalive_statistics_recorder) {
     // This logic comes from
     // content::ResourceDispatcherHostImpl::BeginRequestInternal.
-    bool exhausted = false;
     // This is needed because we want to know whether the request is initiated
     // by fetch() or not. We hope that we can unify these restrictions and
     // remove the reference to fetch_request_context_type in the future.
@@ -117,16 +117,18 @@
             kMaxKeepaliveConnectionsPerProcessForFetchAPI) {
       exhausted = true;
     }
-    if (exhausted) {
-      if (client) {
-        URLLoaderCompletionStatus status;
-        status.error_code = net::ERR_INSUFFICIENT_RESOURCES;
-        status.exists_in_cache = false;
-        status.completion_time = base::TimeTicks::Now();
-        client->OnComplete(status);
-      }
-      return;
-    }
+  }
+
+  if (!context_->CanCreateLoader(params_->process_id))
+    exhausted = true;
+
+  if (exhausted) {
+    URLLoaderCompletionStatus status;
+    status.error_code = net::ERR_INSUFFICIENT_RESOURCES;
+    status.exists_in_cache = false;
+    status.completion_time = base::TimeTicks::Now();
+    client->OnComplete(status);
+    return;
   }
 
   auto loader = std::make_unique<URLLoader>(
diff --git a/services/ws/ime/ime_driver_bridge.cc b/services/ws/ime/ime_driver_bridge.cc
index a7e5a7d..4c0b6d6 100644
--- a/services/ws/ime/ime_driver_bridge.cc
+++ b/services/ws/ime/ime_driver_bridge.cc
@@ -9,9 +9,24 @@
 
 namespace ws {
 
-IMEDriverBridge::IMEDriverBridge() {}
+struct IMEDriverBridge::Request {
+  Request() = default;
+  Request(Request&& other) = default;
+  Request(mojom::InputMethodRequest input_method_request,
+          mojom::TextInputClientPtr client,
+          mojom::SessionDetailsPtr details)
+      : input_method_request(std::move(input_method_request)),
+        client(std::move(client)),
+        details(std::move(details)) {}
+  ~Request() = default;
 
-IMEDriverBridge::~IMEDriverBridge() {}
+  mojom::InputMethodRequest input_method_request;
+  mojom::TextInputClientPtr client;
+  mojom::SessionDetailsPtr details;
+};
+
+IMEDriverBridge::IMEDriverBridge() = default;
+IMEDriverBridge::~IMEDriverBridge() = default;
 
 void IMEDriverBridge::AddBinding(mojom::IMEDriverRequest request) {
   bindings_.AddBinding(this, std::move(request));
@@ -27,20 +42,28 @@
   driver_ = std::move(driver);
 
   while (!pending_requests_.empty()) {
-    driver_->StartSession(std::move(pending_requests_.front()));
+    auto& request = pending_requests_.front();
+    driver_->StartSession(std::move(request.input_method_request),
+                          std::move(request.client),
+                          std::move(request.details));
     pending_requests_.pop();
   }
 }
 
-void IMEDriverBridge::StartSession(mojom::StartSessionDetailsPtr details) {
+void IMEDriverBridge::StartSession(
+    mojom::InputMethodRequest input_method_request,
+    mojom::TextInputClientPtr client,
+    mojom::SessionDetailsPtr details) {
   if (driver_.get()) {
     // TODO(moshayedi): crbug.com/634431. This will forward all calls from
     // clients to the driver as they are. We may need to check |caret_bounds|
     // parameter of InputMethod::OnCaretBoundsChanged() here and limit them to
     // client's focused window.
-    driver_->StartSession(std::move(details));
+    driver_->StartSession(std::move(input_method_request), std::move(client),
+                          std::move(details));
   } else {
-    pending_requests_.push(std::move(details));
+    pending_requests_.push(Request(std::move(input_method_request),
+                                   std::move(client), std::move(details)));
   }
 }
 
diff --git a/services/ws/ime/ime_driver_bridge.h b/services/ws/ime/ime_driver_bridge.h
index cf66cf33..4d9ee4f 100644
--- a/services/ws/ime/ime_driver_bridge.h
+++ b/services/ws/ime/ime_driver_bridge.h
@@ -22,13 +22,18 @@
   void SetDriver(mojom::IMEDriverPtr driver);
 
  private:
+  // Holds the params to start an IME session.
+  struct Request;
+
   // mojom::IMEDriver:
-  void StartSession(mojom::StartSessionDetailsPtr details) override;
+  void StartSession(mojom::InputMethodRequest input_method_request,
+                    mojom::TextInputClientPtr client,
+                    mojom::SessionDetailsPtr details) override;
 
   mojo::BindingSet<mojom::IMEDriver> bindings_;
   mojom::IMEDriverPtr driver_;
 
-  base::queue<mojom::StartSessionDetailsPtr> pending_requests_;
+  base::queue<Request> pending_requests_;
 
   DISALLOW_COPY_AND_ASSIGN(IMEDriverBridge);
 };
diff --git a/services/ws/ime/ime_unittest.cc b/services/ws/ime/ime_unittest.cc
index 452acec..43d3534b 100644
--- a/services/ws/ime/ime_unittest.cc
+++ b/services/ws/ime/ime_unittest.cc
@@ -114,16 +114,14 @@
 // Tests sending a KeyEvent to the IMEDriver through the Mus IMEDriver.
 TEST_F(IMEAppTest, ProcessKeyEvent) {
   ws::mojom::InputMethodPtr input_method;
-  ws::mojom::StartSessionDetailsPtr details =
-      ws::mojom::StartSessionDetails::New();
-  TestTextInputClient client(MakeRequest(&details->client));
-  details->input_method_request = MakeRequest(&input_method);
-  details->state = ws::mojom::TextInputState::New();
-  details->state->text_input_type = ui::TEXT_INPUT_TYPE_TEXT;
-  details->state->text_input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
-  details->state->text_direction = base::i18n::LEFT_TO_RIGHT;
-  details->state->text_input_flags = 0;
-  ime_driver_->StartSession(std::move(details));
+  ws::mojom::SessionDetailsPtr details = ws::mojom::SessionDetails::New();
+  details->state = ws::mojom::TextInputState::New(ui::TEXT_INPUT_TYPE_TEXT,
+                                                  ui::TEXT_INPUT_MODE_DEFAULT,
+                                                  base::i18n::LEFT_TO_RIGHT, 0);
+  ws::mojom::TextInputClientPtr client_ptr;
+  TestTextInputClient client(MakeRequest(&client_ptr));
+  ime_driver_->StartSession(MakeRequest(&input_method), std::move(client_ptr),
+                            std::move(details));
 
   // Send character key event.
   ui::KeyEvent char_event('A', ui::VKEY_A, ui::DomCode::NONE, 0);
diff --git a/services/ws/ime/test_ime_driver/test_ime_driver.cc b/services/ws/ime/test_ime_driver/test_ime_driver.cc
index 5ecfbb61..3c86c2f0 100644
--- a/services/ws/ime/test_ime_driver/test_ime_driver.cc
+++ b/services/ws/ime/test_ime_driver/test_ime_driver.cc
@@ -70,11 +70,11 @@
 
 TestIMEDriver::~TestIMEDriver() {}
 
-void TestIMEDriver::StartSession(mojom::StartSessionDetailsPtr details) {
-  mojo::MakeStrongBinding(
-      std::make_unique<TestInputMethod>(
-          mojom::TextInputClientPtr(std::move(details->client))),
-      std::move(details->input_method_request));
+void TestIMEDriver::StartSession(mojom::InputMethodRequest input_method_request,
+                                 mojom::TextInputClientPtr client,
+                                 mojom::SessionDetailsPtr details) {
+  mojo::MakeStrongBinding(std::make_unique<TestInputMethod>(std::move(client)),
+                          std::move(input_method_request));
 }
 
 }  // namespace test
diff --git a/services/ws/ime/test_ime_driver/test_ime_driver.h b/services/ws/ime/test_ime_driver/test_ime_driver.h
index 011a80b..90bf7d8a 100644
--- a/services/ws/ime/test_ime_driver/test_ime_driver.h
+++ b/services/ws/ime/test_ime_driver/test_ime_driver.h
@@ -22,7 +22,9 @@
 
  private:
   // mojom::IMEDriver:
-  void StartSession(mojom::StartSessionDetailsPtr details) override;
+  void StartSession(mojom::InputMethodRequest input_method_request,
+                    mojom::TextInputClientPtr client,
+                    mojom::SessionDetailsPtr details) override;
 
   DISALLOW_COPY_AND_ASSIGN(TestIMEDriver);
 };
diff --git a/services/ws/public/mojom/ime/ime.mojom b/services/ws/public/mojom/ime/ime.mojom
index b81c56d9..7056815 100644
--- a/services/ws/public/mojom/ime/ime.mojom
+++ b/services/ws/public/mojom/ime/ime.mojom
@@ -120,14 +120,33 @@
   int32 text_input_flags;  // A bitfield of ui::TextInputFlags.
 };
 
-// Parameters needed to start an IME session.
-struct StartSessionDetails {
-  TextInputClient client;
-  InputMethod& input_method_request;
+// Represents how a text client gets focused. Corresponds to
+// ui::TextInputClient::FocusReason.
+enum FocusReason {
+  kNone,   // Not focused.
+  kMouse,  // User initiated with mouse.
+  kTouch,  // User initiated with touch.
+  kPen,    // User initiated with pen.
+  kOther,  // All other reasons (e.g. system initiated, mouse)
+};
 
-  // Initial details about |client| required by IMEDriver.
+// Detailed data of an IME session.
+struct SessionDetails {
+  // State of the text input client.
   TextInputState state;
+
+  // Caret bounds of the text input client.
   gfx.mojom.Rect caret_bounds;
+
+  // How the text input client was focused.
+  FocusReason focus_reason;
+
+  // ukm::SourceId for identifying the text input client.
+  int64 client_source_for_metrics;
+
+  // Whether the text entered into this text input client should be used to
+  // improve IME suggestions.
+  bool should_do_learning;
 };
 
 // A service which provides the IMEDriver interface is responsible for doing
@@ -135,7 +154,9 @@
 // the client via the InputMethod interface, and sends composition events to
 // the client via the TextInputClient.
 interface IMEDriver {
-  StartSession(StartSessionDetails details);
+  StartSession(InputMethod& input_method_request,
+               TextInputClient client,
+               SessionDetails details);
 };
 
 // An IME driver register should register itself to Mus using the IMERegistrar
diff --git a/services/ws/public/mojom/ime/ime.typemap b/services/ws/public/mojom/ime/ime.typemap
index 17053a8..fdaa2f7 100644
--- a/services/ws/public/mojom/ime/ime.typemap
+++ b/services/ws/public/mojom/ime/ime.typemap
@@ -7,6 +7,7 @@
   "//ui/base/ime/candidate_window.h",
   "//ui/base/ime/composition_text.h",
   "//ui/base/ime/ime_text_span.h",
+  "//ui/base/ime/text_input_client.h",
   "//ui/base/ime/text_input_mode.h",
   "//ui/base/ime/text_input_type.h",
 ]
@@ -26,6 +27,7 @@
   "ws.mojom.CandidateWindowEntry=ui::CandidateWindow::Entry",
   "ws.mojom.CandidateWindowProperties=ui::CandidateWindow::CandidateWindowProperty",
   "ws.mojom.CompositionText=ui::CompositionText",
+  "ws.mojom.FocusReason=ui::TextInputClient::FocusReason",
   "ws.mojom.ImeTextSpan=ui::ImeTextSpan",
   "ws.mojom.ImeTextSpanThickness=ui::ImeTextSpan::Thickness",
   "ws.mojom.TextInputMode=ui::TextInputMode",
diff --git a/services/ws/public/mojom/ime/ime_struct_traits.cc b/services/ws/public/mojom/ime/ime_struct_traits.cc
index d101ee7a..54d0f3bb 100644
--- a/services/ws/public/mojom/ime/ime_struct_traits.cc
+++ b/services/ws/public/mojom/ime/ime_struct_traits.cc
@@ -70,6 +70,53 @@
 }
 
 // static
+ws::mojom::FocusReason
+EnumTraits<ws::mojom::FocusReason, ui::TextInputClient::FocusReason>::ToMojom(
+    ui::TextInputClient::FocusReason input) {
+  switch (input) {
+    case ui::TextInputClient::FOCUS_REASON_NONE:
+      return ws::mojom::FocusReason::kNone;
+    case ui::TextInputClient::FOCUS_REASON_MOUSE:
+      return ws::mojom::FocusReason::kMouse;
+    case ui::TextInputClient::FOCUS_REASON_TOUCH:
+      return ws::mojom::FocusReason::kTouch;
+    case ui::TextInputClient::FOCUS_REASON_PEN:
+      return ws::mojom::FocusReason::kPen;
+    case ui::TextInputClient::FOCUS_REASON_OTHER:
+      return ws::mojom::FocusReason::kOther;
+  }
+
+  NOTREACHED();
+  return ws::mojom::FocusReason::kNone;
+}
+
+// static
+bool EnumTraits<ws::mojom::FocusReason, ui::TextInputClient::FocusReason>::
+    FromMojom(ws::mojom::FocusReason input,
+              ui::TextInputClient::FocusReason* out) {
+  switch (input) {
+    case ws::mojom::FocusReason::kNone:
+      *out = ui::TextInputClient::FOCUS_REASON_NONE;
+      return true;
+    case ws::mojom::FocusReason::kMouse:
+      *out = ui::TextInputClient::FOCUS_REASON_MOUSE;
+      return true;
+    case ws::mojom::FocusReason::kTouch:
+      *out = ui::TextInputClient::FOCUS_REASON_TOUCH;
+      return true;
+    case ws::mojom::FocusReason::kPen:
+      *out = ui::TextInputClient::FOCUS_REASON_PEN;
+      return true;
+    case ws::mojom::FocusReason::kOther:
+      *out = ui::TextInputClient::FOCUS_REASON_OTHER;
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+// static
 ws::mojom::ImeTextSpanType
 EnumTraits<ws::mojom::ImeTextSpanType, ui::ImeTextSpan::Type>::ToMojom(
     ui::ImeTextSpan::Type ime_text_span_type) {
diff --git a/services/ws/public/mojom/ime/ime_struct_traits.h b/services/ws/public/mojom/ime/ime_struct_traits.h
index 8e87acb..916d4dc 100644
--- a/services/ws/public/mojom/ime/ime_struct_traits.h
+++ b/services/ws/public/mojom/ime/ime_struct_traits.h
@@ -9,6 +9,7 @@
 #include "ui/base/ime/candidate_window.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_text_span.h"
+#include "ui/base/ime/text_input_client.h"
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 
@@ -86,6 +87,13 @@
 };
 
 template <>
+struct EnumTraits<ws::mojom::FocusReason, ui::TextInputClient::FocusReason> {
+  static ws::mojom::FocusReason ToMojom(ui::TextInputClient::FocusReason input);
+  static bool FromMojom(ws::mojom::FocusReason input,
+                        ui::TextInputClient::FocusReason* out);
+};
+
+template <>
 struct StructTraits<ws::mojom::ImeTextSpanDataView, ui::ImeTextSpan> {
   static ui::ImeTextSpan::Type type(const ui::ImeTextSpan& c) { return c.type; }
   static uint32_t start_offset(const ui::ImeTextSpan& c) {
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index ed9bab9c..01b2011e1 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -74,7 +74,7 @@
 // unless the default is overridden (by passing {sdpSemantics:'plan-b'} as the
 // argument).
 const base::Feature kRTCUnifiedPlanByDefault{"RTCUnifiedPlanByDefault",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Determines if the SDP attrbute extmap-allow-mixed should be offered by
 // default or not. The default value can be overridden by passing
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index 706bf8fa..4aaeca30 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -141,6 +141,7 @@
     "platform/modules/media_capabilities/web_media_decoding_configuration.h",
     "platform/modules/media_capabilities/web_video_configuration.h",
     "platform/modules/mediastream/platform_media_stream_source.h",
+    "platform/modules/mediastream/platform_media_stream_track.h",
     "platform/modules/notifications/web_notification_action.h",
     "platform/modules/notifications/web_notification_constants.h",
     "platform/modules/notifications/web_notification_data.h",
diff --git a/third_party/blink/public/platform/modules/mediastream/platform_media_stream_source.h b/third_party/blink/public/platform/modules/mediastream/platform_media_stream_source.h
index ad7f35e5..86f2954 100644
--- a/third_party/blink/public/platform/modules/mediastream/platform_media_stream_source.h
+++ b/third_party/blink/public/platform/modules/mediastream/platform_media_stream_source.h
@@ -88,10 +88,10 @@
  private:
   MediaStreamDevice device_;
   SourceStoppedCallback stop_callback_;
-#if INSIDE_BLINK
-  GC_PLUGIN_IGNORE("http://crbug.com/409526")
-#endif
-  MediaStreamSource* owner_ = nullptr;
+  blink::WebPrivatePtr<MediaStreamSource,
+                       kWebPrivatePtrDestructionSameThread,
+                       WebPrivatePtrStrength::kWeak>
+      owner_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformMediaStreamSource);
 };
diff --git a/third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h b/third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h
new file mode 100644
index 0000000..686b8ff
--- /dev/null
+++ b/third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h
@@ -0,0 +1,51 @@
+// Copyright 2014 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 THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_MEDIASTREAM_PLATFORM_MEDIA_STREAM_TRACK_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_MEDIASTREAM_PLATFORM_MEDIA_STREAM_TRACK_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/public/platform/web_media_stream_track.h"
+
+namespace blink {
+
+// PlatformMediaStreamTrack is a Chrome representation of
+// blink::WebMediaStreamTrack. It is owned by blink::WebMediaStreamTrack as
+// blink::WebMediaStreamTrack::ExtraData.
+class BLINK_PLATFORM_EXPORT PlatformMediaStreamTrack {
+ public:
+  explicit PlatformMediaStreamTrack(bool is_local_track);
+  virtual ~PlatformMediaStreamTrack();
+
+  static PlatformMediaStreamTrack* GetTrack(
+      const blink::WebMediaStreamTrack& track);
+
+  virtual void SetEnabled(bool enabled) = 0;
+
+  virtual void SetContentHint(
+      blink::WebMediaStreamTrack::ContentHintType content_hint) = 0;
+
+  // If |callback| is not null, it is invoked when the track has stopped.
+  virtual void StopAndNotify(base::OnceClosure callback) = 0;
+
+  void Stop() { StopAndNotify(base::OnceClosure()); }
+
+  // TODO(hta): Make method pure virtual when all tracks have the method.
+  virtual void GetSettings(blink::WebMediaStreamTrack::Settings& settings) {}
+
+  bool is_local_track() const { return is_local_track_; }
+
+ private:
+  const bool is_local_track_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PlatformMediaStreamTrack);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_MEDIASTREAM_PLATFORM_MEDIA_STREAM_TRACK_H_
diff --git a/third_party/blink/public/platform/web_media_stream_track.h b/third_party/blink/public/platform/web_media_stream_track.h
index 4c56f061..3e38e6c8 100644
--- a/third_party/blink/public/platform/web_media_stream_track.h
+++ b/third_party/blink/public/platform/web_media_stream_track.h
@@ -25,6 +25,8 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_STREAM_TRACK_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEDIA_STREAM_TRACK_H_
 
+#include <memory>
+
 #include "base/optional.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
@@ -34,9 +36,9 @@
 
 class MediaStreamComponent;
 class MediaStreamTrack;
+class PlatformMediaStreamTrack;
 class WebAudioSourceProvider;
 class WebMediaConstraints;
-class WebMediaStream;
 class WebMediaStreamSource;
 class WebString;
 
@@ -103,13 +105,6 @@
     base::Optional<CursorCaptureType> cursor;
   };
 
-  class TrackData {
-   public:
-    TrackData() = default;
-    virtual ~TrackData() = default;
-    virtual void GetSettings(Settings&) = 0;
-  };
-
   enum class ContentHintType {
     kNone,
     kAudioSpeech,
@@ -146,12 +141,9 @@
   BLINK_PLATFORM_EXPORT WebMediaConstraints Constraints() const;
   BLINK_PLATFORM_EXPORT void SetConstraints(const WebMediaConstraints&);
 
-  // Extra data associated with this WebMediaStream.
-  // If non-null, the extra data pointer will be deleted when the object is
-  // destroyed.  Setting the track data pointer will cause any existing non-null
-  // track data pointer to be deleted.
-  BLINK_PLATFORM_EXPORT TrackData* GetTrackData() const;
-  BLINK_PLATFORM_EXPORT void SetTrackData(TrackData*);
+  BLINK_PLATFORM_EXPORT PlatformMediaStreamTrack* GetPlatformTrack() const;
+  BLINK_PLATFORM_EXPORT void SetPlatformTrack(
+      std::unique_ptr<PlatformMediaStreamTrack>);
 
   // The lifetime of the WebAudioSourceProvider should outlive the
   // WebMediaStreamTrack, and clients are responsible for calling
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 148ea2ff..177112d 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -65,7 +65,6 @@
 #include "third_party/blink/public/web/web_frame.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
-#include "third_party/blink/public/web/web_global_object_reuse_policy.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
 #include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_icon_url.h"
@@ -396,9 +395,16 @@
   // The provisional datasource is now committed.  The first part of the
   // response body has been received, and the encoding of the response
   // body is known.
+  // The mojo::ScopedMessagePipeHandle is a DocumentInterfaceBroker handle. When
+  // a load commits and a new Document is created, Blink creates a new
+  // DocumentInterfaceBroker endpoint to ensure that interface requests in the
+  // newly committed Document are associated with the correct origin (even if
+  // the origin of the old and the new Document are the same). The one
+  // exception is if the Window object is reused; in that case, the old
+  // DocumentInterfaceBroker handle will be reused, and the endpoint won't be
+  // bound to any requests.
   virtual void DidCommitProvisionalLoad(const WebHistoryItem&,
                                         WebHistoryCommitType,
-                                        WebGlobalObjectReusePolicy,
                                         mojo::ScopedMessagePipeHandle) {}
 
   // The frame's document has just been initialized.
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 7bced7dd..2a1ca29 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1953,6 +1953,7 @@
     "html/image_document_test.cc",
     "html/imports/html_import_sheets_test.cc",
     "html/lazy_load_frame_observer_test.cc",
+    "html/lazy_load_image_observer_test.cc",
     "html/link_element_loading_test.cc",
     "html/link_rel_attribute_test.cc",
     "html/list_item_ordinal_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index b8521faa..7627124ae 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -78,13 +78,16 @@
         document.GetFrame()->IsClientLoFiAllowed(params.GetResourceRequest())) {
       params.SetClientLoFiPlaceholder();
     }
-    cached_image_ = StyleFetchedImage::Create(
-        document, params,
+    bool is_lazily_loaded =
+        image_request_optimization == FetchParameters::kDeferImageLoad &&
         // Only http/https images are eligible to be lazily loaded.
-        params.Url().ProtocolIsInHTTPFamily() &&
-            image_request_optimization == FetchParameters::kDeferImageLoad);
-  }
+        params.Url().ProtocolIsInHTTPFamily();
+    if (is_lazily_loaded)
+      params.SetLazyImageDeferred();
 
+    cached_image_ =
+        StyleFetchedImage::Create(document, params, is_lazily_loaded);
+  }
   return cached_image_.Get();
 }
 
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index 6a6519c2..ea99b13 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -1088,6 +1088,13 @@
       *this);
 }
 
+bool CSSSelector::FollowsPart() const {
+  const CSSSelector* previous = TagHistory();
+  if (!previous)
+    return false;
+  return previous->GetPseudoType() == kPseudoPart;
+}
+
 bool CSSSelector::NeedsUpdatedDistribution() const {
   return ForAnyInTagHistory(
       [](const CSSSelector& selector) -> bool {
diff --git a/third_party/blink/renderer/core/css/css_selector.h b/third_party/blink/renderer/core/css/css_selector.h
index 72f1d831..8396c2d 100644
--- a/third_party/blink/renderer/core/css/css_selector.h
+++ b/third_party/blink/renderer/core/css/css_selector.h
@@ -385,6 +385,8 @@
   bool HasContentPseudo() const;
   bool HasSlottedPseudo() const;
   bool HasDeepCombinatorOrShadowPseudo() const;
+  // Returns true if the immediately preceeding simple selector is ::part.
+  bool FollowsPart() const;
   bool NeedsUpdatedDistribution() const;
   bool HasPseudoIs() const;
   bool HasPseudoWhere() const;
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index 7e7a1d4..1bc35b3 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -453,7 +453,9 @@
 CSSPrimitiveValue* ConsumeAngle(
     CSSParserTokenRange& range,
     const CSSParserContext* context,
-    base::Optional<WebFeature> unitless_zero_feature) {
+    base::Optional<WebFeature> unitless_zero_feature,
+    double minimum_value,
+    double maximum_value) {
   // Ensure that we have a context for counting the
   // unitless_zero_feature if it is requested.
   DCHECK(context || !unitless_zero_feature);
@@ -479,12 +481,32 @@
   }
   CalcParser calc_parser(range, kValueRangeAll);
   if (const CSSCalcValue* calculation = calc_parser.Value()) {
-    if (calculation->Category() == kCalcAngle)
-      return calc_parser.ConsumeValue();
+    if (calculation->Category() != kCalcAngle)
+      return nullptr;
+    if (CSSPrimitiveValue* result = calc_parser.ConsumeValue()) {
+      if (result->GetDoubleValue() < minimum_value) {
+        return CSSPrimitiveValue::Create(minimum_value,
+                                         result->TypeWithCalcResolved());
+      }
+      if (result->GetDoubleValue() > maximum_value) {
+        return CSSPrimitiveValue::Create(maximum_value,
+                                         result->TypeWithCalcResolved());
+      }
+      return result;
+    }
   }
   return nullptr;
 }
 
+CSSPrimitiveValue* ConsumeAngle(
+    CSSParserTokenRange& range,
+    const CSSParserContext* context,
+    base::Optional<WebFeature> unitless_zero_feature) {
+  return ConsumeAngle(range, context, std::move(unitless_zero_feature),
+                      std::numeric_limits<double>::lowest(),
+                      std::numeric_limits<double>::max());
+}
+
 CSSPrimitiveValue* ConsumeTime(CSSParserTokenRange& range,
                                ValueRange value_range) {
   const CSSParserToken& token = range.Peek();
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
index dc58c66..9dbf89c 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h
@@ -67,6 +67,12 @@
     CSSParserTokenRange&,
     const CSSParserContext*,
     base::Optional<WebFeature> unitless_zero_feature);
+CSSPrimitiveValue* ConsumeAngle(
+    CSSParserTokenRange&,
+    const CSSParserContext*,
+    base::Optional<WebFeature> unitless_zero_feature,
+    double minimum_value,
+    double maximum_value);
 CSSPrimitiveValue* ConsumeTime(CSSParserTokenRange&, ValueRange);
 CSSPrimitiveValue* ConsumeResolution(CSSParserTokenRange&);
 
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index b7c96bbf..84c642d 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/svg/svg_parsing_error.h"
 #include "third_party/blink/renderer/core/svg/svg_path_utilities.h"
 #include "third_party/blink/renderer/platform/animation/timing_function.h"
+#include "third_party/blink/renderer/platform/fonts/font_selection_types.h"
 #include "third_party/blink/renderer/platform/geometry/length.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -1390,8 +1391,8 @@
   CSSIdentifierValue* oblique_identifier =
       css_property_parser_helpers::ConsumeIdent<CSSValueOblique>(range);
 
-  CSSPrimitiveValue* start_angle =
-      css_property_parser_helpers::ConsumeAngle(range, nullptr, base::nullopt);
+  CSSPrimitiveValue* start_angle = css_property_parser_helpers::ConsumeAngle(
+      range, nullptr, base::nullopt, MinObliqueValue(), MaxObliqueValue());
   if (!start_angle)
     return oblique_identifier;
   if (!IsAngleWithinLimits(start_angle))
@@ -1403,8 +1404,8 @@
     return CSSFontStyleRangeValue::Create(*oblique_identifier, *value_list);
   }
 
-  CSSPrimitiveValue* end_angle =
-      css_property_parser_helpers::ConsumeAngle(range, nullptr, base::nullopt);
+  CSSPrimitiveValue* end_angle = css_property_parser_helpers::ConsumeAngle(
+      range, nullptr, base::nullopt, MinObliqueValue(), MaxObliqueValue());
   if (!end_angle || !IsAngleWithinLimits(end_angle))
     return nullptr;
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 649913ba..036ce67 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -321,11 +321,24 @@
   collector.FinishAddingAuthorRulesForTreeScope();
 }
 
+void StyleResolver::MatchPseudoPartRulesForUAHost(
+    const Element& element,
+    ElementRuleCollector& collector) {
+  if (element.ShadowPseudoId() != "-webkit-input-placeholder")
+    return;
+
+  // We allow ::placeholder pseudo element after ::part(). See
+  // MatchSlottedRulesForUAHost for a more detailed explanation.
+  DCHECK(element.OwnerShadowHost());
+  MatchPseudoPartRules(*element.OwnerShadowHost(), collector);
+}
+
 void StyleResolver::MatchPseudoPartRules(const Element& element,
                                          ElementRuleCollector& collector) {
   if (!RuntimeEnabledFeatures::CSSPartPseudoElementEnabled())
     return;
 
+  MatchPseudoPartRulesForUAHost(element, collector);
   DOMTokenList* part = element.GetPart();
   if (!part)
     return;
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index fe4ae0d..4c64d6e 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -157,6 +157,7 @@
   // This matches `::part` selectors. It looks in ancestor scopes as far as
   // part mapping requires.
   void MatchPseudoPartRules(const Element&, ElementRuleCollector&);
+  void MatchPseudoPartRulesForUAHost(const Element&, ElementRuleCollector&);
   void MatchScopedRulesV0(const Element&,
                           ElementRuleCollector&,
                           ScopedStyleResolver*);
diff --git a/third_party/blink/renderer/core/css/rule_set.cc b/third_party/blink/renderer/core/css/rule_set.cc
index b9352b85..dcdca93 100644
--- a/third_party/blink/renderer/core/css/rule_set.cc
+++ b/third_party/blink/renderer/core/css/rule_set.cc
@@ -214,9 +214,13 @@
       focus_pseudo_class_rules_.push_back(rule_data);
       return true;
     case CSSSelector::kPseudoPlaceholder:
-      AddToRuleSet(AtomicString("-webkit-input-placeholder"),
-                   EnsurePendingRules()->shadow_pseudo_element_rules,
-                   rule_data);
+      if (it->FollowsPart()) {
+        part_pseudo_rules_.push_back(rule_data);
+      } else {
+        AddToRuleSet(AtomicString("-webkit-input-placeholder"),
+                     EnsurePendingRules()->shadow_pseudo_element_rules,
+                     rule_data);
+      }
       return true;
     case CSSSelector::kPseudoHost:
     case CSSSelector::kPseudoHostContext:
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index a86a862..5aa3450f 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -465,7 +465,7 @@
     }
 
     web_frame_->Client()->DidCommitProvisionalLoad(
-        WebHistoryItem(item), commit_type, global_object_reuse_policy,
+        WebHistoryItem(item), commit_type,
         document_interface_broker_request.PassMessagePipe());
     if (web_frame_->GetFrame()->IsLocalRoot()) {
       // This update should be sent as soon as loading the new document begins
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index e25d03d..2124b05 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -4495,7 +4495,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
-                                WebGlobalObjectReusePolicy,
                                 mojo::ScopedMessagePipeHandle) override {
     Frame()->View()->ResetScrollAndScaleState();
   }
@@ -6652,7 +6651,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
-                                WebGlobalObjectReusePolicy,
                                 mojo::ScopedMessagePipeHandle) override {
     did_load_ = true;
   }
@@ -9483,7 +9481,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType history_commit_type,
-                                WebGlobalObjectReusePolicy,
                                 mojo::ScopedMessagePipeHandle) override {
     history_commit_type_ = history_commit_type;
     remote_frame_->Swap(Frame());
@@ -9700,7 +9697,6 @@
   // frame_test_helpers::TestWebFrameClient:
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType history_commit_type,
-                                WebGlobalObjectReusePolicy,
                                 mojo::ScopedMessagePipeHandle) override {
     history_commit_type_ = history_commit_type;
   }
@@ -10605,7 +10601,6 @@
   }
   void DidCommitProvisionalLoad(const WebHistoryItem&,
                                 WebHistoryCommitType,
-                                WebGlobalObjectReusePolicy,
                                 mojo::ScopedMessagePipeHandle) override {
     EXPECT_EQ(2, callback_count_++);
   }
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 0918eb6..7837510 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1275,14 +1275,12 @@
     if (scrolled_object != GetLayoutView() &&
         !layout_object->IsDescendantOf(&scrolled_object))
       continue;
-    // An object needs to be repainted on scroll when it has background-
-    // attachment:fixed, unless the background will be separately composited
-    // i.e. when a LayoutView paints backgrounds only into scrolling contents.
+    // An object needs to repaint the background on scroll when it has
+    // background-attachment:fixed unless the object is the LayoutView and the
+    // background is not painted on the scrolling contents.
     if (layout_object == GetLayoutView() &&
-        GetLayoutView()->GetBackgroundPaintLocation() ==
-            kBackgroundPaintInScrollingContents &&
-        (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
-         GetLayoutView()->Compositor()->PreferCompositingToLCDTextEnabled()))
+        !(GetLayoutView()->GetBackgroundPaintLocation() &
+          kBackgroundPaintInScrollingContents))
       continue;
     layout_object->SetBackgroundNeedsFullPaintInvalidation();
   }
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
new file mode 100644
index 0000000..887dae0
--- /dev/null
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
@@ -0,0 +1,99 @@
+// 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 "third_party/blink/renderer/core/html/lazy_load_image_observer.h"
+
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/style/style_image.h"
+#include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
+#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
+#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+namespace blink {
+
+namespace {
+
+class LazyLoadImagesSimTest : public SimTest {
+ protected:
+  LazyLoadImagesSimTest() : scoped_lazy_image_loading_for_test_(true) {}
+  void SetLazyLoadEnabled(bool enabled) {
+    WebView().GetPage()->GetSettings().SetLazyLoadEnabled(enabled);
+  }
+
+  void LoadMainResource() {
+    SimRequest main_resource("https://example.com/", "text/html");
+    LoadURL("https://example.com/");
+
+    main_resource.Complete(String::Format(R"HTML(
+          <style>
+          #deferred_image {
+            height:200px;
+            background-image: url('img.jpg');
+          }
+          </style>
+          <div style='height:10000px;'></div>
+          <div id="deferred_image"></div>
+        )HTML"));
+  }
+
+  void ExpectCSSBackgroundImageDeferredState(bool deferred) {
+    const ComputedStyle* deferred_image_style =
+        GetDocument().getElementById("deferred_image")->GetComputedStyle();
+    EXPECT_TRUE(deferred_image_style->HasBackgroundImage());
+    bool is_background_image_found = false;
+    for (const FillLayer* background_layer =
+             &deferred_image_style->BackgroundLayers();
+         background_layer; background_layer = background_layer->Next()) {
+      if (StyleImage* deferred_image = background_layer->GetImage()) {
+        EXPECT_TRUE(deferred_image->IsImageResource());
+        EXPECT_EQ(deferred, deferred_image->IsLazyloadPossiblyDeferred());
+        EXPECT_NE(deferred, deferred_image->IsLoaded());
+        is_background_image_found = true;
+      }
+    }
+    EXPECT_TRUE(is_background_image_found);
+  }
+
+ private:
+  ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test_;
+};
+
+TEST_F(LazyLoadImagesSimTest, CSSBackgroundImageLoadedWithoutLazyLoad) {
+  SetLazyLoadEnabled(false);
+  SimRequest image_resource("https://example.com/img.jpg", "image/jpeg");
+  LoadMainResource();
+  image_resource.Complete("");
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  ExpectCSSBackgroundImageDeferredState(false);
+}
+
+TEST_F(LazyLoadImagesSimTest, CSSBackgroundImageDeferredWithLazyLoad) {
+  SetLazyLoadEnabled(true);
+  LoadMainResource();
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+
+  ExpectCSSBackgroundImageDeferredState(true);
+
+  // Scroll down until the background image is visible.
+  GetDocument().View()->LayoutViewport()->SetScrollOffset(
+      ScrollOffset(0, 10000), kProgrammaticScroll);
+  SimRequest image_resource("https://example.com/img.jpg", "image/jpeg");
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  image_resource.Complete("");
+  ExpectCSSBackgroundImageDeferredState(false);
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index ebb69de..1605378 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -994,12 +994,6 @@
 bool DocumentLoader::WillLoadUrlAsEmpty(const KURL& url) {
   if (url.IsEmpty())
     return true;
-  // Usually, we load urls with about: scheme as empty.
-  // However, about:srcdoc is only used as a marker for non-existent
-  // url of iframes with srcdoc attribute, which have possibly non-empty
-  // content of the srcdoc attribute used as document's html.
-  if (url.IsAboutSrcdocURL())
-    return false;
   return SchemeRegistry::ShouldLoadURLSchemeAsEmptyDocument(url.Protocol());
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index 2954f79c3..fc4a1d50 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -938,15 +938,8 @@
   EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation());
   EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
   EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate());
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    // In CAP, we assume the view's fixed attachment background is composited
-    // at this time and doesn't need paint invalidation on view scroll.
-    EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation());
-    EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
-  } else {
-    EXPECT_TRUE(GetLayoutView().ShouldDoFullPaintInvalidation());
-    EXPECT_TRUE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
-  }
+  EXPECT_TRUE(GetLayoutView().ShouldDoFullPaintInvalidation());
+  EXPECT_TRUE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
   EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
   UpdateAllLifecyclePhasesForTest();
 
diff --git a/third_party/blink/renderer/core/paint/view_painter_test.cc b/third_party/blink/renderer/core/paint/view_painter_test.cc
index 4b9ec5c7..7a72558 100644
--- a/third_party/blink/renderer/core/paint/view_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/view_painter_test.cc
@@ -106,8 +106,6 @@
   SkRect rect = static_cast<const cc::DrawRectOp*>(*it)->rect;
   if (prefer_compositing_to_lcd_text) {
     EXPECT_EQ(SkRect::MakeXYWH(0, 0, 800, 600), rect);
-  } else if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    EXPECT_EQ(SkRect::MakeXYWH(0, 0, 800, 600), rect);
   } else {
     EXPECT_EQ(SkRect::MakeXYWH(scroll_offset.Width(), scroll_offset.Height(),
                                800, 600),
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
index dc930dc1..3753be8 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.cc
@@ -23,7 +23,7 @@
 namespace blink {
 
 AnimationWorklet::AnimationWorklet(Document* document)
-    : Worklet(document), scope_id_(NextId()), last_animation_id_(0) {}
+    : Worklet(document), worklet_id_(NextId()), last_animation_id_(0) {}
 
 AnimationWorklet::~AnimationWorklet() = default;
 
@@ -38,7 +38,7 @@
 
   Document* document = To<Document>(GetExecutionContext());
   AnimationWorkletProxyClient* proxy_client =
-      AnimationWorkletProxyClient::FromDocument(document, scope_id_);
+      AnimationWorkletProxyClient::FromDocument(document, worklet_id_);
 
   WorkerClients* worker_clients = WorkerClients::Create();
   ProvideAnimationWorkletProxyClientTo(worker_clients, proxy_client);
@@ -53,7 +53,7 @@
 WorkletAnimationId AnimationWorklet::NextWorkletAnimationId() {
   // Id starts from 1. This way it safe to use it as key in hashmap with default
   // key traits.
-  return {.scope_id = scope_id_, .animation_id = ++last_animation_id_};
+  return {.worklet_id = worklet_id_, .animation_id = ++last_animation_id_};
 }
 
 void AnimationWorklet::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
index 5cd1e17..aa4abd1 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet.h
@@ -32,7 +32,7 @@
  private:
   // Unique id associated with this worklet that is used by cc to identify all
   // animations associated it.
-  int scope_id_;
+  int worklet_id_;
   int last_animation_id_;
 
   // Implements Worklet.
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
index e3f052b..ed1c349 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
@@ -18,14 +18,14 @@
     "AnimationWorkletProxyClient";
 
 AnimationWorkletProxyClient::AnimationWorkletProxyClient(
-    int scope_id,
+    int worklet_id,
     base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
         compositor_mutator_dispatcher,
     scoped_refptr<base::SingleThreadTaskRunner> compositor_mutator_runner,
     base::WeakPtr<AnimationWorkletMutatorDispatcherImpl>
         main_thread_mutator_dispatcher,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_mutator_runner)
-    : scope_id_(scope_id), state_(RunState::kUninitialized) {
+    : worklet_id_(worklet_id), state_(RunState::kUninitialized) {
   DCHECK(IsMainThread());
   mutator_items_.emplace_back(std::move(compositor_mutator_dispatcher),
                               std::move(compositor_mutator_runner));
@@ -116,9 +116,9 @@
     std::unique_ptr<AnimationWorkletInput> input) {
   DCHECK(input);
 #if DCHECK_IS_ON()
-  DCHECK(input->ValidateScope(scope_id_))
+  DCHECK(input->ValidateId(worklet_id_))
       << "Input has state that does not belong to this global scope: "
-      << scope_id_;
+      << worklet_id_;
 #endif
 
   if (!global_scope_)
@@ -134,7 +134,7 @@
 // static
 AnimationWorkletProxyClient* AnimationWorkletProxyClient::FromDocument(
     Document* document,
-    int scope_id) {
+    int worklet_id) {
   WebLocalFrameImpl* local_frame =
       WebLocalFrameImpl::FromFrame(document->GetFrame());
 
@@ -151,7 +151,7 @@
               .EnsureMainThreadMutatorDispatcher(&main_thread_host_queue);
 
   return MakeGarbageCollected<AnimationWorkletProxyClient>(
-      scope_id, std::move(compositor_mutator_dispatcher),
+      worklet_id, std::move(compositor_mutator_dispatcher),
       std::move(compositor_host_queue),
       std::move(main_thread_mutator_dispatcher),
       std::move(main_thread_host_queue));
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
index 357e6ab..193707f 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.h
@@ -38,7 +38,7 @@
   // This client is hooked to the given |mutatee|, on the given
   // |mutatee_runner|.
   explicit AnimationWorkletProxyClient(
-      int scope_id,
+      int worklet_id,
       base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> compositor_mutatee,
       scoped_refptr<base::SingleThreadTaskRunner> compositor_mutatee_runner,
       base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> main_thread_mutatee,
@@ -51,15 +51,15 @@
 
   // AnimationWorkletMutator:
   // These methods are invoked on the animation worklet thread.
-  int GetScopeId() const override { return scope_id_; }
+  int GetWorkletId() const override { return worklet_id_; }
   std::unique_ptr<AnimationWorkletOutput> Mutate(
       std::unique_ptr<AnimationWorkletInput> input) override;
 
-  static AnimationWorkletProxyClient* FromDocument(Document*, int scope_id);
+  static AnimationWorkletProxyClient* FromDocument(Document*, int worklet_id);
   static AnimationWorkletProxyClient* From(WorkerClients*);
 
  private:
-  const int scope_id_;
+  const int worklet_id_;
 
   struct MutatorItem {
     base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> mutator_dispatcher;
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
index f095085..3d4f0e1 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
@@ -134,12 +134,12 @@
   worklet_animation_->UpdateInputState(state.get());
   // First state request sets the start time and thus current time should be 0.
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(id.scope_id);
+      state->TakeWorkletState(id.worklet_id);
   EXPECT_NEAR(0, input->added_and_updated_animations[0].current_time, error);
   state.reset(new AnimationWorkletDispatcherInput);
   GetDocument().GetAnimationClock().ResetTimeForTesting(second_ticks);
   worklet_animation_->UpdateInputState(state.get());
-  input = state->TakeWorkletState(id.scope_id);
+  input = state->TakeWorkletState(id.worklet_id);
   EXPECT_NEAR(123.4, input->updated_animations[0].current_time, error);
 }
 
@@ -186,14 +186,14 @@
       std::make_unique<AnimationWorkletDispatcherInput>();
   worklet_animation->UpdateInputState(state.get());
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(id.scope_id);
+      state->TakeWorkletState(id.worklet_id);
 
   EXPECT_NEAR(40, input->added_and_updated_animations[0].current_time, error);
   state.reset(new AnimationWorkletDispatcherInput);
 
   scrollable_area->SetScrollOffset(ScrollOffset(0, 70), kProgrammaticScroll);
   worklet_animation->UpdateInputState(state.get());
-  input = state->TakeWorkletState(id.scope_id);
+  input = state->TakeWorkletState(id.worklet_id);
   EXPECT_NEAR(70, input->updated_animations[0].current_time, error);
 }
 
@@ -216,7 +216,7 @@
       std::make_unique<AnimationWorkletDispatcherInput>();
   worklet_animation_->UpdateInputState(state.get());
   std::unique_ptr<AnimationWorkletInput> input =
-      state->TakeWorkletState(id.scope_id);
+      state->TakeWorkletState(id.worklet_id);
   EXPECT_EQ(input->peeked_animations.size(), 1u);
   EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
   EXPECT_EQ(input->updated_animations.size(), 0u);
@@ -227,7 +227,7 @@
   AnimationWorkletOutput::AnimationState output(id);
   worklet_animation_->SetOutputState(output);
   worklet_animation_->UpdateInputState(state.get());
-  input = state->TakeWorkletState(id.scope_id);
+  input = state->TakeWorkletState(id.worklet_id);
   EXPECT_EQ(input->peeked_animations.size(), 1u);
   EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
   EXPECT_EQ(input->updated_animations.size(), 0u);
@@ -241,14 +241,14 @@
   output_with_value.local_times = local_times;
   worklet_animation_->SetOutputState(output_with_value);
   worklet_animation_->UpdateInputState(state.get());
-  input = state->TakeWorkletState(id.scope_id);
+  input = state->TakeWorkletState(id.worklet_id);
   EXPECT_FALSE(input);
   state.reset(new AnimationWorkletDispatcherInput);
 
   // Input time changes. Need to peek again.
   GetDocument().GetAnimationClock().ResetTimeForTesting(second_ticks);
   worklet_animation_->UpdateInputState(state.get());
-  input = state->TakeWorkletState(id.scope_id);
+  input = state->TakeWorkletState(id.worklet_id);
   EXPECT_EQ(input->peeked_animations.size(), 1u);
   EXPECT_EQ(input->added_and_updated_animations.size(), 0u);
   EXPECT_EQ(input->updated_animations.size(), 0u);
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
index 99214c7..44f9078b 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_P2P_QUIC_TRANSPORT_H_
 
+#include "base/logging.h"
 #include "third_party/webrtc/rtc_base/ssl_fingerprint.h"
 
 namespace blink {
@@ -21,6 +22,28 @@
 // This object should be run entirely on the webrtc worker thread.
 class P2PQuicTransport {
  public:
+  // A config used when starting the QUIC handshake.
+  struct StartConfig final {
+    explicit StartConfig(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
+                             remote_fingerprints_in)
+        : remote_fingerprints(std::move(remote_fingerprints_in)) {
+      DCHECK_GT(remote_fingerprints.size(), 0u);
+    }
+
+    explicit StartConfig(const std::string pre_shared_key_in)
+        : pre_shared_key(pre_shared_key_in) {
+      DCHECK(!pre_shared_key.empty());
+    }
+
+    // These fingerprints are used to verify the self signed remote certificate
+    // used in the QUIC handshake. See:
+    // https://w3c.github.io/webrtc-quic/#quic-transport*
+    std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints;
+
+    // The pre shared key to be used in the handshake.
+    const std::string pre_shared_key;
+  };
+
   // Used for receiving callbacks from the P2PQuicTransport regarding QUIC
   // connection changes, handshake success/failures and new QuicStreams being
   // added from the remote side.
@@ -51,15 +74,13 @@
 
   // Closes the QuicConnection and sends a close frame to the remote side.
   // This will trigger P2PQuicTransport::Delegate::OnRemoteClosed() on the
-  // remote side.
+  // remote side. This must not be called before Start().
   virtual void Stop() = 0;
 
-  // Starts the QUIC handshake negotiation and sets the remote fingerprints
-  // that were signaled through a secure channel. These fingerprints are used to
-  // verify the self signed remote certificate used in the QUIC handshake. See:
-  // https://w3c.github.io/webrtc-quic/#quic-transport*
-  virtual void Start(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
-                         remote_fingerprints) = 0;
+  // Starts the QUIC handshake negotiation. If this is a client perspective
+  // this means initiating the QUIC handshake with a CHLO, while for
+  // a server perspective this means now listening and responding to a CHLO.
+  virtual void Start(StartConfig config) = 0;
 
   // Creates a new outgoing stream. This stream is owned by the
   // P2PQuicTransport. Its lifetime is managed by the P2PQuicTransport,
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
index 12bdd06..e902a082 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.cc
@@ -290,8 +290,6 @@
       NOTREACHED();
       break;
   }
-  InitializeCryptoStream();
-  packet_transport_->SetReceiveDelegate(this);
 }
 
 P2PQuicTransportImpl::~P2PQuicTransportImpl() {
@@ -299,6 +297,8 @@
 }
 
 void P2PQuicTransportImpl::Stop() {
+  // This shouldn't be called before Start().
+  DCHECK(crypto_stream_);
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (IsClosed()) {
     return;
@@ -312,23 +312,38 @@
       quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
 }
 
-void P2PQuicTransportImpl::Start(
-    std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints) {
+void P2PQuicTransportImpl::Start(StartConfig config) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK_EQ(remote_fingerprints_.size(), 0u);
-  DCHECK_GT(remote_fingerprints.size(), 0u);
-  if (IsClosed()) {
-    // We could have received a close from the remote side before calling this.
-    return;
+  // Either the remote fingerprints are being verified or a pre shared key is
+  // set.
+  DCHECK(config.remote_fingerprints.size() > 0u ||
+         !config.pre_shared_key.empty());
+  DCHECK(!crypto_stream_);
+
+  remote_fingerprints_ = std::move(config.remote_fingerprints);
+  switch (perspective_) {
+    case quic::Perspective::IS_CLIENT: {
+      crypto_client_config_->set_pre_shared_key(config.pre_shared_key);
+      break;
+    }
+    case quic::Perspective::IS_SERVER: {
+      crypto_server_config_->set_pre_shared_key(config.pre_shared_key);
+      break;
+    }
+    default:
+      NOTREACHED();
+      break;
   }
-  // These will be used to verify the remote certificate during the handshake.
-  remote_fingerprints_ = std::move(remote_fingerprints);
+
+  InitializeCryptoStream();
 
   if (perspective_ == quic::Perspective::IS_CLIENT) {
     quic::QuicCryptoClientStream* client_crypto_stream =
         static_cast<quic::QuicCryptoClientStream*>(crypto_stream_.get());
     client_crypto_stream->CryptoConnect();
   }
+  // Now that crypto streams are setup we are ready to receive QUIC packets.
+  packet_transport_->SetReceiveDelegate(this);
 }
 
 void P2PQuicTransportImpl::OnPacketDataReceived(const char* data,
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
index 5a2292e..ee924141 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_impl.h
@@ -68,26 +68,17 @@
   ~P2PQuicTransportImpl() override;
 
   // P2PQuicTransport overrides.
-
   void Stop() override;
-
-  // Sets the remote fingerprints, and if the the P2PQuicTransportImpl is a
-  // client starts the QUIC handshake . This handshake is currently insecure,
-  // meaning that the certificates used are fake and are not verified. It also
-  // assumes a handshake for a server/client case. This must be called before
-  // creating any streams.
+  // This handshake is currently insecure in the case of using remote
+  // fingerprints to verify the remote certificate. For a secure handshake, set
+  // the pre_shared_key attribute of the |config| before calling this. This
+  // function must be called before creating any streams.
   //
   // TODO(https://crbug.com/874300): Verify both the client and server
   // certificates with the signaled remote fingerprints. Until the TLS 1.3
   // handshake is supported in the QUIC core library we can only verify the
-  // server's certificate, but not the client's. Note that this means
-  // implementing the handshake for a P2P case, in which case verification
-  // completes after both receiving the signaled remote fingerprint and getting
-  // a client hello. Because either can come first, a synchronous call to verify
-  // the remote fingerprint is not possible.
-  void Start(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
-                 remote_fingerprints) override;
-
+  // server's certificate, but not the client's.
+  void Start(StartConfig config) override;
   // Creates an outgoing stream that is owned by the quic::QuicSession.
   P2PQuicStreamImpl* CreateStream() override;
 
@@ -178,11 +169,13 @@
   std::unique_ptr<P2PQuicCryptoConfigFactory> crypto_config_factory_;
 
   std::unique_ptr<quic::QuicCryptoStream> crypto_stream_;
-  // Crypto information. Note that currently the handshake is insecure and these
-  // are not used...
+  // Crypto certificate information. Note that currently the handshake is
+  // insecure and these are not used...
   rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
   std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints_;
 
+  bool pre_shared_key_set_ = false;
+
   quic::Perspective perspective_;
   // Outlives the P2PQuicTransport.
   P2PQuicPacketTransport* packet_transport_;
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
index 48956fe..e0028beb 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_test.cc
@@ -522,33 +522,19 @@
     }
   }
 
-  // Starts the handshake, by setting the remote fingerprints and kicking off
-  // the handshake from the client.
-  void StartHandshake() {
-    std::vector<std::unique_ptr<rtc::SSLFingerprint>> server_fingerprints;
-    server_fingerprints.emplace_back(rtc::SSLFingerprint::Create(
-        "sha-256", server_peer_->certificate()->identity()));
-    // The server side doesn't currently need call this to set the remote
-    // fingerprints, but once P2P certificate verification is supported in the
-    // TLS 1.3 handshake this will ben necessary.
-    server_peer_->quic_transport()->Start(std::move(server_fingerprints));
-
-    std::vector<std::unique_ptr<rtc::SSLFingerprint>> client_fingerprints;
-    client_fingerprints.emplace_back(rtc::SSLFingerprint::Create(
-        "sha-256", client_peer_->certificate()->identity()));
-    client_peer_->quic_transport()->Start(std::move(client_fingerprints));
-  }
-
   // Sets up an initial handshake and connection between peers.
+  // This is done using a pre shared key.
   void Connect() {
     CallbackRunLoop run_loop(runner());
-
     EXPECT_CALL(*client_peer_->quic_transport_delegate(), OnConnected())
         .WillOnce(FireCallback(run_loop.CreateCallback()));
     EXPECT_CALL(*server_peer_->quic_transport_delegate(), OnConnected())
         .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-    StartHandshake();
+    server_peer_->quic_transport()->Start(
+        P2PQuicTransport::StartConfig("foobar"));
+    client_peer_->quic_transport()->Start(
+        P2PQuicTransport::StartConfig("foobar"));
     run_loop.RunUntilCallbacksFired();
   }
 
@@ -660,10 +646,21 @@
   std::unique_ptr<QuicPeerForTest> server_peer_;
 };
 
-// Tests that we can connect two quic transports.
-TEST_F(P2PQuicTransportTest, HandshakeConnectsPeers) {
+// Tests that we can connect two quic transports using pre shared keys.
+TEST_F(P2PQuicTransportTest, HandshakeConnectsPeersWithPreSharedKeys) {
   Initialize();
-  Connect();
+
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnConnected())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnConnected())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  server_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig("foobar"));
+  client_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig("foobar"));
+  run_loop.RunUntilCallbacksFired();
 
   EXPECT_TRUE(client_peer()->quic_transport()->IsEncryptionEstablished());
   EXPECT_TRUE(client_peer()->quic_transport()->IsCryptoHandshakeConfirmed());
@@ -671,6 +668,38 @@
   EXPECT_TRUE(server_peer()->quic_transport()->IsEncryptionEstablished());
 }
 
+// Tests that we can connect two quic transports using remote certificate
+// fingerprints. Note that the fingerprints aren't currently used for
+// verification.
+TEST_F(P2PQuicTransportTest, HandshakeConnectsPeersWithRemoteCertificates) {
+  Initialize();
+
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnConnected())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnConnected())
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  // Start the handshake with the remote fingerprints.
+  std::vector<std::unique_ptr<rtc::SSLFingerprint>> server_fingerprints;
+  server_fingerprints.emplace_back(rtc::SSLFingerprint::Create(
+      "sha-256", server_peer()->certificate()->identity()));
+  server_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig(std::move(server_fingerprints)));
+
+  std::vector<std::unique_ptr<rtc::SSLFingerprint>> client_fingerprints;
+  client_fingerprints.emplace_back(rtc::SSLFingerprint::Create(
+      "sha-256", client_peer()->certificate()->identity()));
+  client_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig(std::move(client_fingerprints)));
+
+  run_loop.RunUntilCallbacksFired();
+
+  EXPECT_TRUE(client_peer()->quic_transport()->IsEncryptionEstablished());
+  EXPECT_TRUE(client_peer()->quic_transport()->IsCryptoHandshakeConfirmed());
+  EXPECT_TRUE(server_peer()->quic_transport()->IsCryptoHandshakeConfirmed());
+  EXPECT_TRUE(server_peer()->quic_transport()->IsEncryptionEstablished());
+}
 // Tests the standard case for the server side closing the connection.
 TEST_F(P2PQuicTransportTest, ServerStops) {
   Initialize();
@@ -726,82 +755,6 @@
   ExpectTransportsClosed();
 }
 
-// Tests that when the client closes the connection the subsequent call to
-// StartHandshake() will be ignored.
-TEST_F(P2PQuicTransportTest, ClientStopsBeforeClientStarts) {
-  Initialize();
-  CallbackRunLoop run_loop(runner());
-  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
-      .WillOnce(FireCallback(run_loop.CreateCallback()));
-  client_peer()->quic_transport()->Stop();
-  StartHandshake();
-  run_loop.RunUntilCallbacksFired();
-
-  ExpectConnectionNotEstablished();
-  ExpectTransportsClosed();
-}
-
-// Tests that if the server closes the connection before the client starts the
-// handshake, the client side will already be closed and Start() will be
-// ignored.
-TEST_F(P2PQuicTransportTest, ServerStopsBeforeClientStarts) {
-  Initialize();
-  CallbackRunLoop run_loop(runner());
-  EXPECT_CALL(*client_peer()->quic_transport_delegate(), OnRemoteStopped())
-      .WillOnce(FireCallback(run_loop.CreateCallback()));
-  EXPECT_CALL(*server_peer()->quic_transport_delegate(), OnRemoteStopped())
-      .Times(0);
-
-  server_peer()->quic_transport()->Stop();
-  StartHandshake();
-  run_loop.RunUntilCallbacksFired();
-
-  ExpectConnectionNotEstablished();
-  ExpectTransportsClosed();
-}
-
-// Tests that when the server's connection fails and then a handshake is
-// attempted the transports will not become connected.
-TEST_F(P2PQuicTransportTest, ClientConnectionClosesBeforeHandshake) {
-  Initialize();
-  CallbackRunLoop run_loop(runner());
-  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
-              OnConnectionFailed(_, _))
-      .WillOnce(FireCallback(run_loop.CreateCallback()));
-  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
-              OnConnectionFailed(_, _))
-      .WillOnce(FireCallback(run_loop.CreateCallback()));
-
-  client_connection()->CloseConnection(
-      quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
-      quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-  StartHandshake();
-  run_loop.RunUntilCallbacksFired();
-
-  ExpectConnectionNotEstablished();
-}
-
-// Tests that when the server's connection fails and then a handshake is
-// attempted the transports will not become connected.
-TEST_F(P2PQuicTransportTest, ServerConnectionClosesBeforeHandshake) {
-  Initialize();
-  CallbackRunLoop run_loop(runner());
-  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
-              OnConnectionFailed(_, _))
-      .WillOnce(FireCallback(run_loop.CreateCallback()));
-  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
-              OnConnectionFailed(_, _))
-      .WillOnce(FireCallback(run_loop.CreateCallback()));
-
-  server_connection()->CloseConnection(
-      quic::QuicErrorCode::QUIC_INTERNAL_ERROR, "internal error",
-      quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-  StartHandshake();
-  run_loop.RunUntilCallbacksFired();
-
-  ExpectConnectionNotEstablished();
-}
-
 // Tests that the appropriate callbacks are fired when the handshake fails.
 TEST_F(P2PQuicTransportTest, HandshakeFailure) {
   InitializeWithFailingProofVerification();
@@ -813,13 +766,38 @@
               OnConnectionFailed(_, _))
       .WillOnce(FireCallback(run_loop.CreateCallback()));
 
-  StartHandshake();
+  server_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig("foobar"));
+  client_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig("foobar"));
   run_loop.RunUntilCallbacksFired();
 
   ExpectConnectionNotEstablished();
   ExpectTransportsClosed();
 }
 
+// Tests that the handshake fails if the pre shared keys don't match.
+// In this case the handshake finishes, but the connection fails because packets
+// can't be decrypted.
+TEST_F(P2PQuicTransportTest, HandshakeFailsBecauseKeysDontMatch) {
+  Initialize();
+  CallbackRunLoop run_loop(runner());
+  EXPECT_CALL(*client_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+  EXPECT_CALL(*server_peer()->quic_transport_delegate(),
+              OnConnectionFailed(_, _))
+      .WillOnce(FireCallback(run_loop.CreateCallback()));
+
+  server_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig("foobar"));
+  client_peer()->quic_transport()->Start(
+      P2PQuicTransport::StartConfig("barfoo"));
+  run_loop.RunUntilCallbacksFired();
+
+  ExpectTransportsClosed();
+}
+
 // Tests that the appropriate callbacks are fired when the client's connection
 // fails after the transports have connected.
 TEST_F(P2PQuicTransportTest, ClientConnectionFailureAfterConnected) {
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
index 5cddf172..0fbda63 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_transport_host.cc
@@ -63,7 +63,10 @@
 void QuicTransportHost::Start(
     std::vector<std::unique_ptr<rtc::SSLFingerprint>> remote_fingerprints) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  quic_transport_->Start(std::move(remote_fingerprints));
+  // TODO(shampson): Update proxy and host to pass through the StartConfig
+  // when PSK support is added to JS bindings.
+  quic_transport_->Start(
+      P2PQuicTransport::StartConfig(std::move(remote_fingerprints)));
 }
 
 void QuicTransportHost::Stop() {
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport.h b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport.h
index fa93bb3..f2935c4 100644
--- a/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport.h
+++ b/third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_transport.h
@@ -17,12 +17,8 @@
 
   // P2PQuicTransport overrides.
   MOCK_METHOD0(Stop, void());
-  void Start(std::vector<std::unique_ptr<rtc::SSLFingerprint>>
-                 remote_fingerprints) override {
-    MockStart(remote_fingerprints);
-  }
-  MOCK_METHOD1(MockStart,
-               void(const std::vector<std::unique_ptr<rtc::SSLFingerprint>>&));
+  void Start(StartConfig config) override { MockStart(config); }
+  MOCK_METHOD1(MockStart, void(const StartConfig&));
   MOCK_METHOD0(CreateStream, P2PQuicStream*());
 };
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index ef83d51d..a1479ef 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -513,6 +513,11 @@
     EXPECT_TRUE(pc->GetTrack(track_component));
 
     RemoveStream(scope, pc, stream);
+    // In Unified Plan, transceivers will still reference the stream even after
+    // it is "removed". To make the GC tests work, clear the stream from tracks
+    // so that the stream does not keep tracks alive.
+    while (!stream->getTracks().IsEmpty())
+      stream->removeTrack(stream->getTracks()[0], scope.GetExceptionState());
   }
 
   // This will destroy |MediaStream|, |MediaStreamTrack| and its
@@ -545,6 +550,11 @@
     EXPECT_TRUE(pc->GetTrack(track_component.Get()));
 
     RemoveStream(scope, pc, stream);
+    // In Unified Plan, transceivers will still reference the stream even after
+    // it is "removed". To make the GC tests work, clear the stream from tracks
+    // so that the stream does not keep tracks alive.
+    while (!stream->getTracks().IsEmpty())
+      stream->removeTrack(stream->getTracks()[0], scope.GetExceptionState());
   }
 
   // This will destroy |MediaStream| and |MediaStreamTrack| (but not
@@ -554,43 +564,6 @@
   EXPECT_FALSE(pc->GetTrack(track_component.Get()));
 }
 
-TEST_F(RTCPeerConnectionTest, GetTrackRemoveStreamAndGCWithPersistentStream) {
-  V8TestingScope scope;
-  Persistent<RTCPeerConnection> pc = CreatePC(scope);
-  EXPECT_EQ("", GetExceptionMessage(scope));
-  ASSERT_TRUE(pc);
-
-  MediaStreamTrack* track =
-      CreateTrack(scope, MediaStreamSource::kTypeAudio, "audioTrack");
-  MediaStreamComponent* track_component = track->Component();
-  Persistent<MediaStream> stream;
-
-  {
-    HeapVector<Member<MediaStreamTrack>> tracks;
-    tracks.push_back(track);
-    stream = MediaStream::Create(scope.GetExecutionContext(), tracks);
-    ASSERT_TRUE(stream);
-
-    EXPECT_FALSE(pc->GetTrack(track_component));
-    AddStream(scope, pc, stream);
-    EXPECT_TRUE(pc->GetTrack(track_component));
-
-    RemoveStream(scope, pc, stream);
-  }
-
-  // With a persistent |MediaStream|, the |MediaStreamTrack| and
-  // |MediaStreamComponent| will not be destroyed and continue to be mapped by
-  // peer connection.
-  WebHeap::CollectAllGarbageForTesting();
-  EXPECT_TRUE(pc->GetTrack(track_component));
-
-  stream = nullptr;
-  // Now |MediaStream|, |MediaStreamTrack| and |MediaStreamComponent| will be
-  // destroyed and the mapping removed from the peer connection.
-  WebHeap::CollectAllGarbageForTesting();
-  EXPECT_FALSE(pc->GetTrack(track_component));
-}
-
 TEST_F(RTCPeerConnectionTest, CheckForComplexSdpWithSdpSemanticsPlanB) {
   V8TestingScope scope;
   Persistent<RTCPeerConnection> pc = CreatePC(scope, "plan-b");
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
index 30b038c..01813cd 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_transport_test.cc
@@ -179,15 +179,13 @@
 
   auto mock_transport = std::make_unique<MockP2PQuicTransport>();
   EXPECT_CALL(*mock_transport, MockStart(_))
-      .WillOnce(
-          Invoke([](const std::vector<std::unique_ptr<rtc::SSLFingerprint>>&
-                        remote_fingerprints) {
-            ASSERT_EQ(1u, remote_fingerprints.size());
-            EXPECT_EQ(kRemoteFingerprintAlgorithm1,
-                      remote_fingerprints[0]->algorithm);
-            EXPECT_EQ(kRemoteFingerprintValue1,
-                      remote_fingerprints[0]->GetRfc4572Fingerprint());
-          }));
+      .WillOnce(Invoke([](const P2PQuicTransport::StartConfig& config) {
+        ASSERT_EQ(1u, config.remote_fingerprints.size());
+        EXPECT_EQ(kRemoteFingerprintAlgorithm1,
+                  config.remote_fingerprints[0]->algorithm);
+        EXPECT_EQ(kRemoteFingerprintValue1,
+                  config.remote_fingerprints[0]->GetRfc4572Fingerprint());
+      }));
   Persistent<RTCQuicTransport> quic_transport =
       CreateQuicTransport(scope, ice_transport, GenerateLocalRTCCertificates(),
                           std::move(mock_transport));
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 81f0babe..145086c 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -506,6 +506,7 @@
     "exported/interface_registry.cc",
     "exported/platform.cc",
     "exported/platform_media_stream_source.cc",
+    "exported/platform_media_stream_track.cc",
     "exported/service_registry.cc",
     "exported/url_conversion.cc",
     "exported/web_audio_bus.cc",
diff --git a/third_party/blink/renderer/platform/exported/platform_media_stream_source.cc b/third_party/blink/renderer/platform/exported/platform_media_stream_source.cc
index a17409a..18880fd 100644
--- a/third_party/blink/renderer/platform/exported/platform_media_stream_source.cc
+++ b/third_party/blink/renderer/platform/exported/platform_media_stream_source.cc
@@ -21,6 +21,7 @@
 
 PlatformMediaStreamSource::~PlatformMediaStreamSource() {
   DCHECK(stop_callback_.is_null());
+  owner_ = nullptr;
 }
 
 void PlatformMediaStreamSource::StopSource() {
@@ -31,13 +32,16 @@
 void PlatformMediaStreamSource::FinalizeStopSource() {
   if (!stop_callback_.is_null())
     base::ResetAndReturn(&stop_callback_).Run(Owner());
-  Owner().SetReadyState(blink::WebMediaStreamSource::kReadyStateEnded);
+  if (Owner())
+    Owner().SetReadyState(blink::WebMediaStreamSource::kReadyStateEnded);
 }
 
 void PlatformMediaStreamSource::SetSourceMuted(bool is_muted) {
   // Although this change is valid only if the ready state isn't already Ended,
   // there's code further along (like in blink::MediaStreamTrack) which filters
   // that out already.
+  if (!Owner())
+    return;
   Owner().SetReadyState(is_muted
                             ? blink::WebMediaStreamSource::kReadyStateMuted
                             : blink::WebMediaStreamSource::kReadyStateLive);
@@ -66,7 +70,7 @@
 
 WebMediaStreamSource PlatformMediaStreamSource::Owner() {
   DCHECK(owner_);
-  return WebMediaStreamSource(owner_);
+  return WebMediaStreamSource(owner_.Get());
 }
 
 #if INSIDE_BLINK
diff --git a/third_party/blink/renderer/platform/exported/platform_media_stream_track.cc b/third_party/blink/renderer/platform/exported/platform_media_stream_track.cc
new file mode 100644
index 0000000..72545706
--- /dev/null
+++ b/third_party/blink/renderer/platform/exported/platform_media_stream_track.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 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 "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
+
+namespace blink {
+
+// static
+PlatformMediaStreamTrack* PlatformMediaStreamTrack::GetTrack(
+    const blink::WebMediaStreamTrack& track) {
+  return track.IsNull() ? nullptr : track.GetPlatformTrack();
+}
+
+PlatformMediaStreamTrack::PlatformMediaStreamTrack(bool is_local_track)
+    : is_local_track_(is_local_track) {}
+
+PlatformMediaStreamTrack::~PlatformMediaStreamTrack() {}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_media_stream_track.cc b/third_party/blink/renderer/platform/exported/web_media_stream_track.cc
index 2e760bc6..23ffc20 100644
--- a/third_party/blink/renderer/platform/exported/web_media_stream_track.cc
+++ b/third_party/blink/renderer/platform/exported/web_media_stream_track.cc
@@ -38,25 +38,6 @@
 
 namespace blink {
 
-namespace {
-
-class TrackDataContainer : public MediaStreamComponent::TrackData {
- public:
-  explicit TrackDataContainer(
-      std::unique_ptr<WebMediaStreamTrack::TrackData> extra_data)
-      : extra_data_(std::move(extra_data)) {}
-
-  WebMediaStreamTrack::TrackData* GetTrackData() { return extra_data_.get(); }
-  void GetSettings(WebMediaStreamTrack::Settings& settings) override {
-    extra_data_->GetSettings(settings);
-  }
-
- private:
-  std::unique_ptr<WebMediaStreamTrack::TrackData> extra_data_;
-};
-
-}  // namespace
-
 const char WebMediaStreamTrack::kResizeModeNone[] = "none";
 const char WebMediaStreamTrack::kResizeModeRescale[] = "crop-and-scale";
 
@@ -128,18 +109,14 @@
   return WebMediaStreamSource(private_->Source());
 }
 
-WebMediaStreamTrack::TrackData* WebMediaStreamTrack::GetTrackData() const {
-  MediaStreamComponent::TrackData* data = private_->GetTrackData();
-  if (!data)
-    return nullptr;
-  return static_cast<TrackDataContainer*>(data)->GetTrackData();
+PlatformMediaStreamTrack* WebMediaStreamTrack::GetPlatformTrack() const {
+  return private_->GetPlatformTrack();
 }
 
-void WebMediaStreamTrack::SetTrackData(TrackData* extra_data) {
+void WebMediaStreamTrack::SetPlatformTrack(
+    std::unique_ptr<PlatformMediaStreamTrack> platform_track) {
   DCHECK(!private_.IsNull());
-
-  private_->SetTrackData(
-      std::make_unique<TrackDataContainer>(base::WrapUnique(extra_data)));
+  private_->SetPlatformTrack(std::move(platform_track));
 }
 
 void WebMediaStreamTrack::SetSourceProvider(WebAudioSourceProvider* provider) {
diff --git a/third_party/blink/renderer/platform/fonts/font_selection_types.h b/third_party/blink/renderer/platform/fonts/font_selection_types.h
index 5fb1723..85d2cb4 100644
--- a/third_party/blink/renderer/platform/fonts/font_selection_types.h
+++ b/third_party/blink/renderer/platform/fonts/font_selection_types.h
@@ -185,6 +185,18 @@
   return italicValue;
 }
 
+static inline const FontSelectionValue& MaxObliqueValue() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, maxObliqueValue,
+                                  (90));
+  return maxObliqueValue;
+}
+
+static inline const FontSelectionValue& MinObliqueValue() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, minObliqueValue,
+                                  (-90));
+  return minObliqueValue;
+}
+
 static inline const FontSelectionValue& BoldThreshold() {
   DEFINE_THREAD_SAFE_STATIC_LOCAL(const FontSelectionValue, boldThreshold,
                                   (600));
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h
index a8d19ed..a35b7834 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h
@@ -15,7 +15,7 @@
  public:
   virtual ~AnimationWorkletMutator() = default;
 
-  virtual int GetScopeId() const = 0;
+  virtual int GetWorkletId() const = 0;
   // Runs the animation frame callback.
   virtual std::unique_ptr<AnimationWorkletOutput> Mutate(
       std::unique_ptr<AnimationWorkletInput>) = 0;
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
index fd75d25..8aa9b1a 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc
@@ -159,11 +159,11 @@
   InputMap input_map;
   for (const auto& pair : mutator_map_) {
     AnimationWorkletMutator* mutator = pair.key;
-    const int scope_id = mutator->GetScopeId();
+    const int worklet_id = mutator->GetWorkletId();
     std::unique_ptr<AnimationWorkletInput> input =
-        mutator_input.TakeWorkletState(scope_id);
+        mutator_input.TakeWorkletState(worklet_id);
     if (input) {
-      input_map.insert(scope_id, std::move(input));
+      input_map.insert(worklet_id, std::move(input));
     }
   }
   return input_map;
@@ -182,9 +182,9 @@
   for (const auto& pair : mutator_map_) {
     AnimationWorkletMutator* mutator = pair.key;
     scoped_refptr<base::SingleThreadTaskRunner> worklet_queue = pair.value;
-    int scope_id = mutator->GetScopeId();
+    int worklet_id = mutator->GetWorkletId();
     DCHECK(!worklet_queue->BelongsToCurrentThread());
-    auto it = mutator_input_map_.find(scope_id);
+    auto it = mutator_input_map_.find(worklet_id);
     if (it == mutator_input_map_.end()) {
       // No input to process.
       on_mutator_done.Run();
diff --git a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
index 6da9706..ff84038 100644
--- a/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
+++ b/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl_test.cc
@@ -53,7 +53,7 @@
     return std::unique_ptr<AnimationWorkletOutput>(MutateRef(*input));
   }
 
-  MOCK_CONST_METHOD0(GetScopeId, int());
+  MOCK_CONST_METHOD0(GetWorkletId, int());
   MOCK_METHOD1(MutateRef,
                AnimationWorkletOutput*(const AnimationWorkletInput&));
 
@@ -123,7 +123,7 @@
   mutator_->RegisterAnimationWorkletMutator(first_mutator,
                                             first_thread->GetTaskRunner());
 
-  EXPECT_CALL(*first_mutator, GetScopeId())
+  EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(Truly(OnlyIncludesAnimation1)))
@@ -144,7 +144,7 @@
   mutator_->RegisterAnimationWorkletMutator(first_mutator,
                                             first_thread->GetTaskRunner());
 
-  EXPECT_CALL(*first_mutator, GetScopeId())
+  EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
@@ -177,7 +177,7 @@
 
   mutator_->RegisterAnimationWorkletMutator(first_mutator,
                                             first_thread->GetTaskRunner());
-  EXPECT_CALL(*first_mutator, GetScopeId())
+  EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_)).Times(1).WillOnce(Return(nullptr));
@@ -195,7 +195,7 @@
 
   mutator_->RegisterAnimationWorkletMutator(first_mutator,
                                             first_thread->GetTaskRunner());
-  EXPECT_CALL(*first_mutator, GetScopeId())
+  EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_))
@@ -232,13 +232,13 @@
   mutator_->RegisterAnimationWorkletMutator(second_mutator,
                                             first_thread->GetTaskRunner());
 
-  EXPECT_CALL(*first_mutator, GetScopeId())
+  EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_))
       .Times(1)
       .WillOnce(Return(new AnimationWorkletOutput()));
-  EXPECT_CALL(*second_mutator, GetScopeId())
+  EXPECT_CALL(*second_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(22));
   EXPECT_CALL(*second_mutator, MutateRef(_))
@@ -267,13 +267,13 @@
   mutator_->RegisterAnimationWorkletMutator(second_mutator,
                                             second_thread->GetTaskRunner());
 
-  EXPECT_CALL(*first_mutator, GetScopeId())
+  EXPECT_CALL(*first_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(11));
   EXPECT_CALL(*first_mutator, MutateRef(_))
       .Times(1)
       .WillOnce(Return(new AnimationWorkletOutput()));
-  EXPECT_CALL(*second_mutator, GetScopeId())
+  EXPECT_CALL(*second_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(22));
   EXPECT_CALL(*second_mutator, MutateRef(_))
@@ -289,9 +289,9 @@
   // Ensure mutator is not invoked after unregistration.
   mutator_->UnregisterAnimationWorkletMutator(first_mutator);
 
-  EXPECT_CALL(*first_mutator, GetScopeId()).Times(0);
+  EXPECT_CALL(*first_mutator, GetWorkletId()).Times(0);
   EXPECT_CALL(*first_mutator, MutateRef(_)).Times(0);
-  EXPECT_CALL(*second_mutator, GetScopeId())
+  EXPECT_CALL(*second_mutator, GetWorkletId())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(22));
   EXPECT_CALL(*second_mutator, MutateRef(_))
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
index 497d88b..01674de 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
@@ -123,6 +123,13 @@
   SetAllowImagePlaceholder();
 }
 
+void FetchParameters::SetLazyImageDeferred() {
+  resource_request_.SetPreviewsState(resource_request_.GetPreviewsState() |
+                                     WebURLRequest::kLazyImageLoadDeferred);
+  DCHECK_EQ(kNone, image_request_optimization_);
+  image_request_optimization_ = kDeferImageLoad;
+}
+
 void FetchParameters::SetAllowImagePlaceholder() {
   DCHECK_EQ(kNone, image_request_optimization_);
   if (!resource_request_.Url().ProtocolIsInHTTPFamily() ||
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
index 229410e..76b6099 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
@@ -182,9 +182,10 @@
   // Client LoFi preview bit.
   void SetClientLoFiPlaceholder();
 
-  // Configures the request to load an image as a placeholder and sets the
-  // lazy image load bit.
+  // Configures the request to load an image as a placeholder or defers the
+  // image and sets the lazy image load bit.
   void SetLazyImagePlaceholder();
+  void SetLazyImageDeferred();
 
   // Configures the request to load an image placeholder if the request is
   // eligible (e.g. the url's protocol is HTTP, etc.). If this request is
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_component.cc b/third_party/blink/renderer/platform/mediastream/media_stream_component.cc
index b05352d..58335b9 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_component.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_component.cc
@@ -65,6 +65,7 @@
                                            MediaStreamSource* source)
     : source_(source), id_(id), unique_id_(GenerateUniqueId()) {
   DCHECK(id_.length());
+  DCHECK(source_);
 }
 
 MediaStreamComponent* MediaStreamComponent::Clone() const {
@@ -79,7 +80,7 @@
 }
 
 void MediaStreamComponent::Dispose() {
-  track_data_.reset();
+  platform_track_.reset();
 }
 
 void MediaStreamComponent::AudioSourceProviderImpl::Wrap(
@@ -90,9 +91,9 @@
 
 void MediaStreamComponent::GetSettings(
     WebMediaStreamTrack::Settings& settings) {
-  DCHECK(track_data_);
+  DCHECK(platform_track_);
   source_->GetSettings(settings);
-  track_data_->GetSettings(settings);
+  platform_track_->GetSettings(settings);
 }
 
 void MediaStreamComponent::SetContentHint(
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_component.h b/third_party/blink/renderer/platform/mediastream/media_stream_component.h
index 8839431c..c8f1a26 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_component.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_component.h
@@ -33,6 +33,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_COMPONENT_H_
 
 #include <memory>
+
+#include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_track.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/renderer/platform/audio/audio_source_provider.h"
@@ -57,16 +59,6 @@
   static int GenerateUniqueId();
 
  public:
-  // This class represents whatever data the Web layer uses to represent
-  // a track. It needs to be able to answer the getSettings question.
-  class TrackData {
-    USING_FAST_MALLOC(TrackData);
-
-   public:
-    virtual void GetSettings(WebMediaStreamTrack::Settings&) = 0;
-    virtual ~TrackData() = default;
-  };
-
   static MediaStreamComponent* Create(MediaStreamSource*);
   static MediaStreamComponent* Create(const String& id, MediaStreamSource*);
 
@@ -103,9 +95,12 @@
     source_provider_.Wrap(provider);
   }
 
-  TrackData* GetTrackData() const { return track_data_.get(); }
-  void SetTrackData(std::unique_ptr<TrackData> track_data) {
-    track_data_ = std::move(track_data);
+  PlatformMediaStreamTrack* GetPlatformTrack() const {
+    return platform_track_.get();
+  }
+  void SetPlatformTrack(
+      std::unique_ptr<PlatformMediaStreamTrack> platform_track) {
+    platform_track_ = std::move(platform_track);
   }
   void GetSettings(WebMediaStreamTrack::Settings&);
 
@@ -143,7 +138,7 @@
   WebMediaStreamTrack::ContentHintType content_hint_ =
       WebMediaStreamTrack::ContentHintType::kNone;
   WebMediaConstraints constraints_;
-  std::unique_ptr<TrackData> track_data_;
+  std::unique_ptr<PlatformMediaStreamTrack> platform_track_;
 };
 
 typedef HeapVector<Member<MediaStreamComponent>> MediaStreamComponentVector;
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 88c1e04..786897f 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -92,8 +92,6 @@
     "main_thread/render_widget_signals.h",
     "main_thread/resource_loading_task_runner_handle_impl.cc",
     "main_thread/resource_loading_task_runner_handle_impl.h",
-    "main_thread/task_cost_estimator.cc",
-    "main_thread/task_cost_estimator.h",
     "main_thread/task_type_names.cc",
     "main_thread/task_type_names.h",
     "main_thread/use_case.h",
@@ -199,7 +197,6 @@
     "main_thread/page_scheduler_impl_unittest.cc",
     "main_thread/queueing_time_estimator_unittest.cc",
     "main_thread/render_widget_signals_unittest.cc",
-    "main_thread/task_cost_estimator_unittest.cc",
     "main_thread/user_model_unittest.cc",
     "worker/compositor_thread_scheduler_unittest.cc",
     "worker/worker_scheduler_proxy_unittest.cc",
diff --git a/third_party/blink/renderer/platform/scheduler/common/features.h b/third_party/blink/renderer/platform/scheduler/common/features.h
index dd978f3..3ab46244 100644
--- a/third_party/blink/renderer/platform/scheduler/common/features.h
+++ b/third_party/blink/renderer/platform/scheduler/common/features.h
@@ -143,22 +143,6 @@
 extern const char PLATFORM_EXPORT kThrottleableTaskTypesListParam[];
 extern const char PLATFORM_EXPORT kFreezableTaskTypesListParam[];
 
-// https://crbug.com/874836: Experiment-controlled removal of input heuristics.
-// Expensive task blocking as a part of input handling heuristics, so disabling
-// input heuristics implicitly disables expensive task blocking. Expensive task
-// blocking is tested separately as it's less risky. Touchstart and
-// non-touchstart input heuristics are separated because non-touchstart are
-// seen as less ricky.
-const base::Feature kDisableExpensiveTaskBlocking{
-    "BlinkSchedulerDisableExpensiveTaskBlocking",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kDisableNonTouchstartInputHeuristics{
-    "BlinkSchedulerDisableNonTouchstartInputHeuristics",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-const base::Feature kDisableTouchstartInputHeuristics{
-    "BlinkSchedulerDisableTouchstartInputHeuristics",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 }  // namespace scheduler
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 47812d32..96df8a3 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -47,14 +47,6 @@
 using base::sequence_manager::TimeDomain;
 
 namespace {
-// The run time of loading tasks is strongly bimodal.  The vast majority are
-// very cheap, but there are usually a handful of very expensive tasks (e.g ~1
-// second on a mobile device) so we take a very pessimistic view when estimating
-// the cost of loading tasks.
-const int kLoadingTaskEstimationSampleCount = 1000;
-const double kLoadingTaskEstimationPercentile = 99;
-const int kTimerTaskEstimationSampleCount = 1000;
-const double kTimerTaskEstimationPercentile = 99;
 const int kShortIdlePeriodDurationSampleCount = 10;
 const double kShortIdlePeriodDurationPercentile = 50;
 // Amount of idle time left in a frame (as a ratio of the vsync interval) above
@@ -297,21 +289,6 @@
       this);
 
   for (auto& pair : task_runners_) {
-    TaskCostEstimator* observer = nullptr;
-    switch (pair.first->queue_class()) {
-      case MainThreadTaskQueue::QueueClass::kLoading:
-        observer = &main_thread_only().loading_task_cost_estimator;
-        break;
-      case MainThreadTaskQueue::QueueClass::kTimer:
-        observer = &main_thread_only().timer_task_cost_estimator;
-        break;
-      default:
-        observer = nullptr;
-    }
-
-    if (observer)
-      pair.first->RemoveTaskObserver(observer);
-
     pair.first->ShutdownTaskQueue();
   }
 
@@ -335,13 +312,7 @@
     const scoped_refptr<MainThreadTaskQueue>& compositor_task_runner,
     const base::TickClock* time_source,
     base::TimeTicks now)
-    : loading_task_cost_estimator(time_source,
-                                  kLoadingTaskEstimationSampleCount,
-                                  kLoadingTaskEstimationPercentile),
-      timer_task_cost_estimator(time_source,
-                                kTimerTaskEstimationSampleCount,
-                                kTimerTaskEstimationPercentile),
-      idle_time_estimator(compositor_task_runner,
+    : idle_time_estimator(compositor_task_runner,
                           time_source,
                           kShortIdlePeriodDurationSampleCount,
                           kShortIdlePeriodDurationPercentile),
@@ -360,11 +331,6 @@
                            "Scheduler.PauseCount",
                            main_thread_scheduler_impl,
                            &main_thread_scheduler_impl->tracing_controller_),
-      expensive_task_policy(ExpensiveTaskPolicy::kRun,
-                            "Scheduler.ExpensiveTaskPolicy",
-                            main_thread_scheduler_impl,
-                            &main_thread_scheduler_impl->tracing_controller_,
-                            ExpensiveTaskPolicyToString),
       rail_mode_for_tracing(current_policy.rail_mode(),
                             "Scheduler.RAILMode",
                             main_thread_scheduler_impl,
@@ -386,30 +352,6 @@
           main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
-      loading_task_estimated_cost(
-          base::TimeDelta(),
-          "Scheduler.LoadingTaskEstimatedCostMs",
-          main_thread_scheduler_impl,
-          &main_thread_scheduler_impl->tracing_controller_,
-          TimeDeltaToMilliseconds),
-      timer_task_estimated_cost(
-          base::TimeDelta(),
-          "Scheduler.TimerTaskEstimatedCostMs",
-          main_thread_scheduler_impl,
-          &main_thread_scheduler_impl->tracing_controller_,
-          TimeDeltaToMilliseconds),
-      loading_tasks_seem_expensive(
-          false,
-          "Scheduler.LoadingTasksSeemExpensive",
-          main_thread_scheduler_impl,
-          &main_thread_scheduler_impl->tracing_controller_,
-          YesNoStateToString),
-      timer_tasks_seem_expensive(
-          false,
-          "Scheduler.TimerTasksSeemExpensive",
-          main_thread_scheduler_impl,
-          &main_thread_scheduler_impl->tracing_controller_,
-          YesNoStateToString),
       blocking_input_expected_soon(
           false,
           "Scheduler.BlockingInputExpectedSoon",
@@ -617,13 +559,6 @@
 
   FrameSchedulerImpl::InitializeTaskTypeQueueTraitsMap(
       frame_task_types_to_queue_traits);
-
-  disable_expensive_task_blocking =
-      base::FeatureList::IsEnabled(kDisableExpensiveTaskBlocking);
-  disable_non_touchstart_input_heuristics =
-      base::FeatureList::IsEnabled(kDisableNonTouchstartInputHeuristics);
-  disable_touchstart_input_heuristics =
-      base::FeatureList::IsEnabled(kDisableTouchstartInputHeuristics);
 }
 
 MainThreadSchedulerImpl::AnyThread::~AnyThread() = default;
@@ -742,12 +677,6 @@
   auto insert_result =
       task_runners_.insert(std::make_pair(task_queue, std::move(voter)));
   auto queue_class = task_queue->queue_class();
-  if (queue_class == MainThreadTaskQueue::QueueClass::kTimer) {
-    task_queue->AddTaskObserver(&main_thread_only().timer_task_cost_estimator);
-  } else if (queue_class == MainThreadTaskQueue::QueueClass::kLoading) {
-    task_queue->AddTaskObserver(
-        &main_thread_only().loading_task_cost_estimator);
-  }
 
   ApplyTaskQueuePolicy(
       task_queue.get(), insert_result.first->second.get(), TaskQueuePolicy(),
@@ -808,20 +737,7 @@
   if (task_queue_throttler_)
     task_queue_throttler_->ShutdownTaskQueue(task_queue.get());
 
-  if (task_runners_.erase(task_queue)) {
-    switch (task_queue->queue_class()) {
-      case MainThreadTaskQueue::QueueClass::kTimer:
-        task_queue->RemoveTaskObserver(
-            &main_thread_only().timer_task_cost_estimator);
-        break;
-      case MainThreadTaskQueue::QueueClass::kLoading:
-        task_queue->RemoveTaskObserver(
-            &main_thread_only().loading_task_cost_estimator);
-        break;
-      default:
-        break;
-    }
-  }
+  task_runners_.erase(task_queue.get());
 }
 
 bool MainThreadSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
@@ -1408,22 +1324,6 @@
   main_thread_only().longest_jank_free_task_duration =
       longest_jank_free_task_duration;
 
-  main_thread_only().loading_task_estimated_cost =
-      main_thread_only().loading_task_cost_estimator.expected_task_duration();
-  bool loading_tasks_seem_expensive =
-      main_thread_only().loading_task_estimated_cost >
-      longest_jank_free_task_duration;
-
-  main_thread_only().timer_task_estimated_cost =
-      main_thread_only().timer_task_cost_estimator.expected_task_duration();
-  bool timer_tasks_seem_expensive =
-      main_thread_only().timer_task_estimated_cost >
-      longest_jank_free_task_duration;
-
-  main_thread_only().timer_tasks_seem_expensive = timer_tasks_seem_expensive;
-  main_thread_only().loading_tasks_seem_expensive =
-      loading_tasks_seem_expensive;
-
   // The |new_policy_duration| is the minimum of |expected_use_case_duration|
   // and |gesture_expected_flag_valid_for_duration| unless one is zero in
   // which case we choose the other.
@@ -1452,7 +1352,6 @@
           kFastCompositingIdleTimeThreshold;
 
   Policy new_policy;
-  ExpensiveTaskPolicy expensive_task_policy = ExpensiveTaskPolicy::kRun;
   new_policy.rail_mode() = v8::PERFORMANCE_ANIMATION;
   new_policy.use_case() = main_thread_only().current_use_case;
 
@@ -1460,7 +1359,6 @@
     case UseCase::kCompositorGesture:
       if (main_thread_only().blocking_input_expected_soon) {
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
-        expensive_task_policy = ExpensiveTaskPolicy::kBlock;
         new_policy.compositor_priority() =
             TaskQueue::QueuePriority::kHighestPriority;
       } else {
@@ -1477,12 +1375,8 @@
       new_policy.compositor_priority() = main_thread_compositing_is_fast
                                              ? TaskQueue::kHighestPriority
                                              : TaskQueue::kNormalPriority;
-      if (main_thread_only().blocking_input_expected_soon) {
+      if (main_thread_only().blocking_input_expected_soon)
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
-        expensive_task_policy = ExpensiveTaskPolicy::kBlock;
-      } else {
-        expensive_task_policy = ExpensiveTaskPolicy::kThrottle;
-      }
       break;
 
     case UseCase::kMainThreadCustomInputHandling:
@@ -1503,12 +1397,8 @@
       // handling over other tasks.
       new_policy.compositor_priority() =
           TaskQueue::QueuePriority::kHighestPriority;
-      if (main_thread_only().blocking_input_expected_soon) {
+      if (main_thread_only().blocking_input_expected_soon)
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
-        expensive_task_policy = ExpensiveTaskPolicy::kBlock;
-      } else {
-        expensive_task_policy = ExpensiveTaskPolicy::kThrottle;
-      }
       break;
 
     case UseCase::kTouchstart:
@@ -1517,8 +1407,6 @@
       new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
       new_policy.loading_queue_policy().is_deferred = true;
       new_policy.timer_queue_policy().is_deferred = true;
-      // NOTE this is a nop due to the above.
-      expensive_task_policy = ExpensiveTaskPolicy::kBlock;
       break;
 
     case UseCase::kNone:
@@ -1527,7 +1415,6 @@
       if (main_thread_only().blocking_input_expected_soon &&
           any_thread().last_gesture_was_compositor_driven) {
         new_policy.rail_mode() = v8::PERFORMANCE_RESPONSE;
-        expensive_task_policy = ExpensiveTaskPolicy::kBlock;
       }
       break;
 
@@ -1545,36 +1432,6 @@
   if (main_thread_only().renderer_hidden)
     new_policy.rail_mode() = v8::PERFORMANCE_IDLE;
 
-  if (expensive_task_policy == ExpensiveTaskPolicy::kBlock &&
-      !main_thread_only().have_seen_a_begin_main_frame) {
-    expensive_task_policy = ExpensiveTaskPolicy::kRun;
-  }
-
-  if (scheduling_settings().disable_expensive_task_blocking)
-    expensive_task_policy = ExpensiveTaskPolicy::kRun;
-
-  switch (expensive_task_policy) {
-    case ExpensiveTaskPolicy::kRun:
-      break;
-
-    case ExpensiveTaskPolicy::kBlock:
-      if (loading_tasks_seem_expensive)
-        new_policy.loading_queue_policy().is_deferred = true;
-      if (timer_tasks_seem_expensive)
-        new_policy.timer_queue_policy().is_deferred = true;
-      break;
-
-    case ExpensiveTaskPolicy::kThrottle:
-      if (loading_tasks_seem_expensive) {
-        new_policy.loading_queue_policy().is_throttled = true;
-      }
-      if (timer_tasks_seem_expensive) {
-        new_policy.timer_queue_policy().is_throttled = true;
-      }
-      break;
-  }
-  main_thread_only().expensive_task_policy = expensive_task_policy;
-
   if (main_thread_only().renderer_pause_count != 0) {
     new_policy.loading_queue_policy().is_paused = true;
     new_policy.timer_queue_policy().is_paused = true;
@@ -1694,8 +1551,7 @@
   // Special case for flings. This is needed because we don't get notification
   // of a fling ending (although we do for cancellation).
   if (any_thread().fling_compositor_escalation_deadline > now &&
-      !any_thread().awaiting_touch_start_response &&
-      !scheduling_settings().disable_non_touchstart_input_heuristics) {
+      !any_thread().awaiting_touch_start_response) {
     *expected_use_case_duration =
         any_thread().fling_compositor_escalation_deadline - now;
     return UseCase::kCompositorGesture;
@@ -1705,8 +1561,7 @@
       any_thread().user_model.TimeLeftInUserGesture(now);
   if (*expected_use_case_duration > base::TimeDelta()) {
     // Has a gesture been fully established?
-    if (any_thread().awaiting_touch_start_response &&
-        !scheduling_settings().disable_touchstart_input_heuristics) {
+    if (any_thread().awaiting_touch_start_response) {
       // No, so arrange for compositor tasks to be run at the highest priority.
       return UseCase::kTouchstart;
     }
@@ -1721,20 +1576,18 @@
     //    stream of input events and has prevented a default gesture from being
     //    started.
     // 4. SYNCHRONIZED_GESTURE where the gesture is processed on both threads.
-    if (!scheduling_settings().disable_non_touchstart_input_heuristics) {
-      if (any_thread().last_gesture_was_compositor_driven) {
-        if (any_thread().begin_main_frame_on_critical_path) {
-          return UseCase::kSynchronizedGesture;
-        } else {
-          return UseCase::kCompositorGesture;
-        }
-      }
-      if (any_thread().default_gesture_prevented) {
-        return UseCase::kMainThreadCustomInputHandling;
+    if (any_thread().last_gesture_was_compositor_driven) {
+      if (any_thread().begin_main_frame_on_critical_path) {
+        return UseCase::kSynchronizedGesture;
       } else {
-        return UseCase::kMainThreadGesture;
+        return UseCase::kCompositorGesture;
       }
     }
+    if (any_thread().default_gesture_prevented) {
+      return UseCase::kMainThreadCustomInputHandling;
+    } else {
+      return UseCase::kMainThreadGesture;
+    }
   }
 
   // Occasionally the meaningful paint fails to be detected, so as a fallback we
@@ -1794,16 +1647,6 @@
   return &helper_;
 }
 
-TaskCostEstimator*
-MainThreadSchedulerImpl::GetLoadingTaskCostEstimatorForTesting() {
-  return &main_thread_only().loading_task_cost_estimator;
-}
-
-TaskCostEstimator*
-MainThreadSchedulerImpl::GetTimerTaskCostEstimatorForTesting() {
-  return &main_thread_only().timer_task_cost_estimator;
-}
-
 IdleTimeEstimator* MainThreadSchedulerImpl::GetIdleTimeEstimatorForTesting() {
   return &main_thread_only().idle_time_estimator;
 }
@@ -2045,22 +1888,6 @@
       "MainThreadScheduler", this, AsValueLocked(helper_.NowTicks()));
 }
 
-// static
-const char* MainThreadSchedulerImpl::ExpensiveTaskPolicyToString(
-    ExpensiveTaskPolicy expensive_task_policy) {
-  switch (expensive_task_policy) {
-    case ExpensiveTaskPolicy::kRun:
-      return "run";
-    case ExpensiveTaskPolicy::kBlock:
-      return "block";
-    case ExpensiveTaskPolicy::kThrottle:
-      return "throttle";
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
-}
-
 std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
 MainThreadSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const {
   helper_.CheckOnValidThread();
@@ -2075,10 +1902,6 @@
       main_thread_only().has_visible_render_widget_with_touch_handler);
   state->SetString("current_use_case",
                    UseCaseToString(main_thread_only().current_use_case));
-  state->SetBoolean("loading_tasks_seem_expensive",
-                    main_thread_only().loading_tasks_seem_expensive);
-  state->SetBoolean("timer_tasks_seem_expensive",
-                    main_thread_only().timer_tasks_seem_expensive);
   state->SetBoolean("begin_frame_not_expected_soon",
                     main_thread_only().begin_frame_not_expected_soon);
   state->SetBoolean(
@@ -2122,14 +1945,6 @@
                     any_thread().last_gesture_was_compositor_driven);
   state->SetBoolean("default_gesture_prevented",
                     any_thread().default_gesture_prevented);
-  state->SetDouble("expected_loading_task_duration",
-                   main_thread_only()
-                       .loading_task_cost_estimator.expected_task_duration()
-                       .InMillisecondsF());
-  state->SetDouble("expected_timer_task_duration",
-                   main_thread_only()
-                       .timer_task_cost_estimator.expected_task_duration()
-                       .InMillisecondsF());
   state->SetBoolean("is_audio_playing", main_thread_only().is_audio_playing);
   state->SetBoolean("virtual_time_stopped",
                     main_thread_only().virtual_time_stopped);
@@ -2165,10 +1980,6 @@
           .InMillisecondsF());
   state->SetBoolean("in_idle_period", any_thread().in_idle_period);
 
-  state->SetString(
-      "expensive_task_policy",
-      ExpensiveTaskPolicyToString(main_thread_only().expensive_task_policy));
-
   any_thread().user_model.AsValueInto(state.get());
   render_widget_scheduler_signals_.AsValueInto(state.get());
 
@@ -2339,8 +2150,6 @@
   any_thread().have_seen_a_blocking_gesture = false;
   any_thread().waiting_for_meaningful_paint = true;
   any_thread().have_seen_input_since_navigation = false;
-  main_thread_only().loading_task_cost_estimator.Clear();
-  main_thread_only().timer_task_cost_estimator.Clear();
   main_thread_only().idle_time_estimator.Clear();
   main_thread_only().have_seen_a_begin_main_frame = false;
   main_thread_only().have_reported_blocking_intervention_since_navigation =
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index 25e448a..04e1717 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -41,7 +41,6 @@
 #include "third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
-#include "third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/use_case.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
@@ -132,10 +131,6 @@
     // entries. This is initialized early with all valid entries. Entries that
     // aren't valid task types, i.e. non-frame level, are base::nullopt.
     FrameTaskTypeToQueueTraitsArray frame_task_types_to_queue_traits;
-
-    bool disable_expensive_task_blocking;
-    bool disable_non_touchstart_input_heuristics;
-    bool disable_touchstart_input_heuristics;
   };
 
   static const char* UseCaseToString(UseCase use_case);
@@ -338,8 +333,6 @@
 
   // Test helpers.
   MainThreadSchedulerHelper* GetSchedulerHelperForTesting();
-  TaskCostEstimator* GetLoadingTaskCostEstimatorForTesting();
-  TaskCostEstimator* GetTimerTaskCostEstimatorForTesting();
   IdleTimeEstimator* GetIdleTimeEstimatorForTesting();
   base::TimeTicks CurrentIdleTaskDeadlineForTesting() const;
   void RunIdleTasksForTesting(base::OnceClosure callback);
@@ -422,8 +415,6 @@
       main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest,
       Tracing);
 
-  enum class ExpensiveTaskPolicy { kRun, kBlock, kThrottle };
-
   enum class TimeDomainType {
     kReal,
     kThrottled,
@@ -695,9 +686,6 @@
       const TaskQueuePolicy& old_task_queue_policy,
       const TaskQueuePolicy& new_task_queue_policy) const;
 
-  static const char* ExpensiveTaskPolicyToString(
-      ExpensiveTaskPolicy expensive_task_policy);
-
   void AddQueueToWakeUpBudgetPool(MainThreadTaskQueue* queue);
 
   void PauseRendererImpl();
@@ -812,8 +800,6 @@
         base::TimeTicks now);
     ~MainThreadOnly();
 
-    TaskCostEstimator loading_task_cost_estimator;
-    TaskCostEstimator timer_task_cost_estimator;
     IdleTimeEstimator idle_time_estimator;
     TraceableState<UseCase, TracingCategoryName::kDefault> current_use_case;
     Policy current_policy;
@@ -825,21 +811,12 @@
         longest_jank_free_task_duration;
     TraceableCounter<int, TracingCategoryName::kInfo>
         renderer_pause_count;  // Renderer is paused if non-zero.
-    TraceableState<ExpensiveTaskPolicy, TracingCategoryName::kInfo>
-        expensive_task_policy;
     TraceableState<v8::RAILMode, TracingCategoryName::kInfo>
         rail_mode_for_tracing;  // Don't use except for tracing.
     TraceableState<bool, TracingCategoryName::kDebug> renderer_hidden;
     TraceableState<bool, TracingCategoryName::kTopLevel> renderer_backgrounded;
     TraceableState<bool, TracingCategoryName::kDefault>
         keep_active_fetch_or_worker;
-    TraceableCounter<base::TimeDelta, TracingCategoryName::kInfo>
-        loading_task_estimated_cost;
-    TraceableCounter<base::TimeDelta, TracingCategoryName::kInfo>
-        timer_task_estimated_cost;
-    TraceableState<bool, TracingCategoryName::kInfo>
-        loading_tasks_seem_expensive;
-    TraceableState<bool, TracingCategoryName::kInfo> timer_tasks_seem_expensive;
     TraceableState<bool, TracingCategoryName::kDefault>
         blocking_input_expected_soon;
     TraceableState<bool, TracingCategoryName::kDebug>
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 4af2aa2..9bb201f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -616,14 +616,6 @@
     return scheduler_->main_thread_only().have_seen_a_begin_main_frame;
   }
 
-  bool LoadingTasksSeemExpensive() {
-    return scheduler_->main_thread_only().loading_tasks_seem_expensive;
-  }
-
-  bool TimerTasksSeemExpensive() {
-    return scheduler_->main_thread_only().timer_tasks_seem_expensive;
-  }
-
   base::TimeTicks EstimatedNextFrameBegin() {
     return scheduler_->main_thread_only().estimated_next_frame_begin;
   }
@@ -808,27 +800,6 @@
   DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImplTest);
 };
 
-class MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics
-    : public MainThreadSchedulerImplTest {
- public:
-  MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics()
-      : MainThreadSchedulerImplTest({kDisableTouchstartInputHeuristics,
-                                     kDisableNonTouchstartInputHeuristics},
-                                    {kPrioritizeCompositingAfterInput}) {}
-  ~MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics() override =
-      default;
-};
-
-class MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics
-    : public MainThreadSchedulerImplTest {
- public:
-  MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics()
-      : MainThreadSchedulerImplTest({kDisableNonTouchstartInputHeuristics},
-                                    {kPrioritizeCompositingAfterInput}) {}
-  ~MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics() override =
-      default;
-};
-
 TEST_F(MainThreadSchedulerImplTest, TestPostDefaultTask) {
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "D1 D2 D3 D4");
@@ -1032,22 +1003,6 @@
   EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase());
 }
 
-TEST_F(MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics,
-       TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler) {
-  std::vector<std::string> run_order;
-  PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  EnableIdleTasks();
-  SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("C1"), std::string("D2"),
-                                   std::string("C2"), std::string("I1")));
-  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
-}
-
 TEST_F(MainThreadSchedulerImplTest,
        TestCompositorPolicy_MainThreadHandlesInput_WithoutScrollUpdates) {
   std::vector<std::string> run_order;
@@ -1214,52 +1169,6 @@
   EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase());
 }
 
-TEST_F(
-    MainThreadSchedulerImplTestWithoutTouchstartInputHeuristics,
-    TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_NoPreventDefault) {
-  std::vector<std::string> run_order;
-  PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  EnableIdleTasks();
-  scheduler_->DidHandleInputEventOnCompositorThread(
-      FakeTouchEvent(blink::WebInputEvent::kTouchStart),
-      InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
-  scheduler_->DidHandleInputEventOnMainThread(
-      FakeTouchEvent(blink::WebInputEvent::kTouchStart),
-      WebInputEventResult::kHandledSystem);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1"),
-                                   std::string("C1"), std::string("D2"),
-                                   std::string("C2"), std::string("I1")));
-  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
-}
-
-TEST_F(
-    MainThreadSchedulerImplTestWithoutNonTouchstartInputHeuristics,
-    TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_NoPreventDefault) {
-  std::vector<std::string> run_order;
-  PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2");
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  EnableIdleTasks();
-  scheduler_->DidHandleInputEventOnCompositorThread(
-      FakeTouchEvent(blink::WebInputEvent::kTouchStart),
-      InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
-  scheduler_->DidHandleInputEventOnMainThread(
-      FakeTouchEvent(blink::WebInputEvent::kTouchStart),
-      WebInputEventResult::kHandledSystem);
-  base::RunLoop().RunUntilIdle();
-  // Because we are still waiting for the touchstart to be processed,
-  // non-essential tasks like loading tasks are blocked.
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("C2"),
-                                   std::string("D1"), std::string("D2"),
-                                   std::string("I1")));
-  EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase());
-}
-
 TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) {
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "I1 D1 C1 D2 C2");
@@ -1299,93 +1208,6 @@
               testing::ElementsAre(std::string("C1"), std::string("T1")));
 }
 
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimersDontRunWhenMainThreadScrolling) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  SimulateExpensiveTasks(timer_task_runner_);
-  DoMainFrame();
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kGestureScrollUpdate);
-
-  PostTestTasks(&run_order, "C1 T1");
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(BlockingInputExpectedSoon());
-  EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
-
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("C1")));
-}
-
-class MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking
-    : public MainThreadSchedulerImplTest {
- public:
-  MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking()
-      : MainThreadSchedulerImplTest({kDisableExpensiveTaskBlocking}, {}) {}
-  ~MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking() override = default;
-};
-
-TEST_F(MainThreadSchedulerImplTestWithoutExpensiveTaskBlocking,
-       ExpensiveTimersRunWhenMainThreadScrolling) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  SimulateExpensiveTasks(timer_task_runner_);
-  DoMainFrame();
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kGestureScrollUpdate);
-
-  PostTestTasks(&run_order, "C1 T1");
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(BlockingInputExpectedSoon());
-  EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase());
-
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("T1")));
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimersDoRunWhenMainThreadInputHandling) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  SimulateExpensiveTasks(timer_task_runner_);
-  DoMainFrame();
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kUndefined);
-
-  PostTestTasks(&run_order, "C1 T1");
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(BlockingInputExpectedSoon());
-  EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
-
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("T1")));
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimersDoRunWhenMainThreadScrolling_AndOnCriticalPath) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  SimulateExpensiveTasks(timer_task_runner_);
-  DoMainFrameOnCriticalPath();
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kGestureScrollBegin);
-
-  PostTestTasks(&run_order, "C1 T1");
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(BlockingInputExpectedSoon());
-  EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
-
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("T1")));
-}
-
 TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicy_Compositor) {
   std::vector<std::string> run_order;
   PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2");
@@ -2686,326 +2508,6 @@
 }
 
 TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveLoadingTasksNotBlockedTillFirstBeginMainFrame) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHaveSeenABlockingGestureForTesting(true);
-  SimulateExpensiveTasks(loading_task_queue_->task_runner());
-  ForceBlockingInputToBeExpectedSoon();
-  PostTestTasks(&run_order, "L1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
-  EXPECT_FALSE(HaveSeenABeginMainframe());
-  EXPECT_TRUE(LoadingTasksSeemExpensive());
-  EXPECT_FALSE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
-
-  // Emit a BeginMainFrame, and the loading task should get blocked.
-  DoMainFrame();
-  run_order.clear();
-
-  PostTestTasks(&run_order, "L1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
-  EXPECT_TRUE(HaveSeenABeginMainframe());
-  EXPECT_TRUE(LoadingTasksSeemExpensive());
-  EXPECT_FALSE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveLoadingTasksNotBlockedIfNoTouchHandler) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(false);
-  DoMainFrame();
-  SimulateExpensiveTasks(loading_task_queue_->task_runner());
-  ForceBlockingInputToBeExpectedSoon();
-  PostTestTasks(&run_order, "L1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
-  EXPECT_TRUE(HaveSeenABeginMainframe());
-  EXPECT_TRUE(LoadingTasksSeemExpensive());
-  EXPECT_FALSE(TimerTasksSeemExpensive());
-  EXPECT_FALSE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("L1"), std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimerTaskBlocked_UseCase_NONE_PreviousCompositorGesture) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHaveSeenABlockingGestureForTesting(true);
-  DoMainFrame();
-  SimulateExpensiveTasks(timer_task_runner_);
-  ForceBlockingInputToBeExpectedSoon();
-
-  PostTestTasks(&run_order, "T1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
-  EXPECT_TRUE(HaveSeenABeginMainframe());
-  EXPECT_FALSE(LoadingTasksSeemExpensive());
-  EXPECT_TRUE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimerTaskNotBlocked_UseCase_NONE_PreviousMainThreadGesture) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  DoMainFrame();
-  SimulateExpensiveTasks(timer_task_runner_);
-
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kGestureScrollBegin);
-  EXPECT_EQ(UseCase::kMainThreadCustomInputHandling,
-            ForceUpdatePolicyAndGetCurrentUseCase());
-
-  scheduler_->DidHandleInputEventOnCompositorThread(
-      FakeInputEvent(blink::WebInputEvent::kTouchEnd),
-      InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
-  scheduler_->DidHandleInputEventOnMainThread(
-      FakeInputEvent(blink::WebInputEvent::kTouchEnd),
-      WebInputEventResult::kHandledSystem);
-
-  test_task_runner_->AdvanceMockTickClock(
-      priority_escalation_after_input_duration() * 2);
-  EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
-
-  PostTestTasks(&run_order, "T1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
-  EXPECT_TRUE(HaveSeenABeginMainframe());
-  EXPECT_FALSE(LoadingTasksSeemExpensive());
-  EXPECT_TRUE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("T1"), std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimerTaskBlocked_UseCase_kCompositorGesture) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHaveSeenABlockingGestureForTesting(true);
-  DoMainFrame();
-  SimulateExpensiveTasks(timer_task_runner_);
-  ForceBlockingInputToBeExpectedSoon();
-  scheduler_->DidAnimateForInputOnCompositorThread();
-
-  PostTestTasks(&run_order, "T1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kCompositorGesture,
-            ForceUpdatePolicyAndGetCurrentUseCase());
-  EXPECT_TRUE(HaveSeenABeginMainframe());
-  EXPECT_FALSE(LoadingTasksSeemExpensive());
-  EXPECT_TRUE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimerTaskBlocked_EvenIfBeginMainFrameNotExpectedSoon) {
-  std::vector<std::string> run_order;
-
-  scheduler_->SetHaveSeenABlockingGestureForTesting(true);
-  DoMainFrame();
-  SimulateExpensiveTasks(timer_task_runner_);
-  ForceBlockingInputToBeExpectedSoon();
-  scheduler_->BeginFrameNotExpectedSoon();
-
-  PostTestTasks(&run_order, "T1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
-  EXPECT_TRUE(HaveSeenABeginMainframe());
-  EXPECT_FALSE(LoadingTasksSeemExpensive());
-  EXPECT_TRUE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveLoadingTasksBlockedIfChildFrameNavigationExpected) {
-  std::vector<std::string> run_order;
-
-  DoMainFrame();
-  scheduler_->SetHaveSeenABlockingGestureForTesting(true);
-  SimulateExpensiveTasks(loading_task_queue_->task_runner());
-  ForceBlockingInputToBeExpectedSoon();
-
-  PostTestTasks(&run_order, "L1 D1");
-  base::RunLoop().RunUntilIdle();
-
-  // The expensive loading task gets blocked.
-  EXPECT_THAT(run_order, testing::ElementsAre(std::string("D1")));
-  EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveLoadingTasksNotBlockedDuringMainThreadGestures) {
-  std::vector<std::string> run_order;
-
-  SimulateExpensiveTasks(loading_task_queue_->task_runner());
-
-  // Loading tasks should not be disabled during main thread user interactions.
-  PostTestTasks(&run_order, "C1 L1");
-
-  // Trigger main_thread_gesture UseCase
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kGestureScrollBegin);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase());
-
-  EXPECT_TRUE(LoadingTasksSeemExpensive());
-  EXPECT_FALSE(TimerTasksSeemExpensive());
-  EXPECT_THAT(run_order,
-              testing::ElementsAre(std::string("C1"), std::string("L1")));
-  EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-}
-
-TEST_F(MainThreadSchedulerImplTest, ModeratelyExpensiveTimer_NotBlocked) {
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kTouchMove);
-  base::RunLoop().RunUntilIdle();
-  for (int i = 0; i < 20; i++) {
-    simulate_timer_task_ran_ = false;
-
-    viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, Now(),
-        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
-        viz::BeginFrameArgs::NORMAL);
-    begin_frame_args.on_critical_path = false;
-    scheduler_->WillBeginFrame(begin_frame_args);
-
-    compositor_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadSchedulerImplTest::
-                           SimulateMainThreadInputHandlingCompositorTask,
-                       base::Unretained(this),
-                       base::TimeDelta::FromMilliseconds(8)));
-    timer_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadSchedulerImplTest::SimulateTimerTask,
-                       base::Unretained(this),
-                       base::TimeDelta::FromMilliseconds(4)));
-
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
-    EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase())
-        << " i = " << i;
-    EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
-    EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
-
-    base::TimeDelta time_till_next_frame = EstimatedNextFrameBegin() - Now();
-    if (time_till_next_frame > base::TimeDelta())
-      test_task_runner_->AdvanceMockTickClock(time_till_next_frame);
-  }
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       FourtyMsTimer_NotBlocked_CompositorScrolling) {
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  base::RunLoop().RunUntilIdle();
-  for (int i = 0; i < 20; i++) {
-    simulate_timer_task_ran_ = false;
-
-    viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, Now(),
-        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
-        viz::BeginFrameArgs::NORMAL);
-    begin_frame_args.on_critical_path = false;
-    scheduler_->WillBeginFrame(begin_frame_args);
-    scheduler_->DidAnimateForInputOnCompositorThread();
-
-    compositor_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &MainThreadSchedulerImplTest::SimulateMainThreadCompositorTask,
-            base::Unretained(this), base::TimeDelta::FromMilliseconds(8)));
-    timer_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadSchedulerImplTest::SimulateTimerTask,
-                       base::Unretained(this),
-                       base::TimeDelta::FromMilliseconds(40)));
-
-    base::RunLoop().RunUntilIdle();
-    EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
-    EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()) << " i = " << i;
-    EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
-    EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
-
-    base::TimeDelta time_till_next_frame = EstimatedNextFrameBegin() - Now();
-    if (time_till_next_frame > base::TimeDelta())
-      test_task_runner_->AdvanceMockTickClock(time_till_next_frame);
-  }
-}
-
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimer_NotBlocked_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) {
-  scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true);
-  SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart,
-                                 blink::WebInputEvent::kTouchMove);
-  base::RunLoop().RunUntilIdle();
-  for (int i = 0; i < 20; i++) {
-    simulate_timer_task_ran_ = false;
-
-    viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, Now(),
-        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
-        viz::BeginFrameArgs::NORMAL);
-    begin_frame_args.on_critical_path = false;
-    scheduler_->WillBeginFrame(begin_frame_args);
-
-    compositor_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadSchedulerImplTest::
-                           SimulateMainThreadInputHandlingCompositorTask,
-                       base::Unretained(this),
-                       base::TimeDelta::FromMilliseconds(8)));
-    timer_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadSchedulerImplTest::SimulateTimerTask,
-                       base::Unretained(this),
-                       base::TimeDelta::FromMilliseconds(10)));
-
-    base::RunLoop().RunUntilIdle();
-    EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase())
-        << " i = " << i;
-    EXPECT_FALSE(LoadingTasksSeemExpensive()) << " i = " << i;
-    if (i == 0) {
-      EXPECT_FALSE(TimerTasksSeemExpensive()) << " i = " << i;
-    } else {
-      EXPECT_TRUE(TimerTasksSeemExpensive()) << " i = " << i;
-    }
-    EXPECT_TRUE(simulate_timer_task_ran_) << " i = " << i;
-
-    base::TimeDelta time_till_next_frame = EstimatedNextFrameBegin() - Now();
-    if (time_till_next_frame > base::TimeDelta())
-      test_task_runner_->AdvanceMockTickClock(time_till_next_frame);
-  }
-}
-
-TEST_F(MainThreadSchedulerImplTest,
        EstimateLongestJankFreeTaskDuration_UseCase_NONE) {
   EXPECT_EQ(UseCase::kNone, CurrentUseCase());
   EXPECT_EQ(rails_response_time(),
@@ -3145,67 +2647,6 @@
 }  // namespace
 
 TEST_F(MainThreadSchedulerImplTest,
-       SYNCHRONIZED_GESTURE_TimerTaskThrottling_task_expensive) {
-  SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart);
-
-  base::TimeTicks first_throttled_run_time =
-      TaskQueueThrottler::AlignedThrottledRunTime(Now());
-
-  size_t count = 0;
-  // With the compositor task taking 10ms, there is not enough time to run this
-  // 7ms timer task in the 16ms frame.
-  timer_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(SlowCountingTask, &count, test_task_runner_, 7,
-                                timer_task_runner_));
-
-  for (int i = 0; i < 1000; i++) {
-    viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
-        BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, Now(),
-        base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
-        viz::BeginFrameArgs::NORMAL);
-    begin_frame_args.on_critical_path = true;
-    scheduler_->WillBeginFrame(begin_frame_args);
-    scheduler_->DidHandleInputEventOnCompositorThread(
-        FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
-        InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
-
-    compositor_task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(&MainThreadSchedulerImplTest::
-                           SimulateMainThreadCompositorAndQuitRunLoopTask,
-                       base::Unretained(this),
-                       base::TimeDelta::FromMilliseconds(10)));
-
-    base::RunLoop().RunUntilIdle();
-    EXPECT_EQ(UseCase::kSynchronizedGesture, CurrentUseCase()) << "i = " << i;
-
-    // We expect the queue to get throttled on the second iteration which is
-    // when the system realizes the task is expensive.
-    bool expect_queue_throttled = (i > 0);
-    EXPECT_EQ(expect_queue_throttled,
-              scheduler_->task_queue_throttler()->IsThrottled(
-                  timer_task_queue_.get()))
-        << "i = " << i;
-
-    if (expect_queue_throttled) {
-      EXPECT_GE(count, 2u);
-    } else {
-      EXPECT_LE(count, 2u);
-    }
-
-    // The task runs twice before the system realizes it's too expensive.
-    bool throttled_task_has_run = count > 2;
-    bool throttled_task_expected_to_have_run =
-        (Now() > first_throttled_run_time);
-    EXPECT_EQ(throttled_task_expected_to_have_run, throttled_task_has_run)
-        << "i = " << i << " count = " << count;
-  }
-
-  // Task is throttled but not completely blocked.
-  EXPECT_EQ(12u, count);
-}
-
-TEST_F(MainThreadSchedulerImplTest,
        SYNCHRONIZED_GESTURE_TimerTaskThrottling_TimersStopped) {
   SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart);
 
@@ -3300,34 +2741,6 @@
   EXPECT_EQ(500u, count);
 }
 
-TEST_F(MainThreadSchedulerImplTest,
-       ExpensiveTimerTaskBlocked_SYNCHRONIZED_GESTURE_GestureExpected) {
-  SimulateExpensiveTasks(timer_task_runner_);
-  scheduler_->DidHandleInputEventOnCompositorThread(
-      FakeTouchEvent(blink::WebInputEvent::kTouchStart),
-      InputEventState::EVENT_FORWARDED_TO_MAIN_THREAD);
-  ForceBlockingInputToBeExpectedSoon();
-
-  // Bump us into SYNCHRONIZED_GESTURE.
-  scheduler_->DidHandleInputEventOnCompositorThread(
-      FakeInputEvent(blink::WebInputEvent::kGestureScrollUpdate),
-      InputEventState::EVENT_CONSUMED_BY_COMPOSITOR);
-
-  viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create(
-      BEGINFRAME_FROM_HERE, 0, next_begin_frame_number_++, Now(),
-      base::TimeTicks(), base::TimeDelta::FromMilliseconds(16),
-      viz::BeginFrameArgs::NORMAL);
-  begin_frame_args.on_critical_path = true;
-  scheduler_->WillBeginFrame(begin_frame_args);
-
-  EXPECT_EQ(UseCase::kSynchronizedGesture,
-            ForceUpdatePolicyAndGetCurrentUseCase());
-
-  EXPECT_TRUE(TimerTasksSeemExpensive());
-  EXPECT_TRUE(BlockingInputExpectedSoon());
-  EXPECT_FALSE(timer_task_queue_->IsQueueEnabled());
-}
-
 TEST_F(MainThreadSchedulerImplTest, DenyLongIdleDuringTouchStart) {
   scheduler_->DidHandleInputEventOnCompositorThread(
       FakeTouchEvent(blink::WebInputEvent::kTouchStart),
@@ -4145,6 +3558,28 @@
                   base::TimeDelta::FromMicroseconds(400 + 50 + 50 + 1250)));
 }
 
+TEST_F(MainThreadSchedulerImplTest, TaskQueueReferenceClearedOnShutdown) {
+  // Ensure that the scheduler clears its references to a task queue after
+  // |shutdown| and doesn't try to update its policies.
+  scoped_refptr<MainThreadTaskQueue> queue1 = scheduler_->NewTimerTaskQueue(
+      MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
+  scoped_refptr<MainThreadTaskQueue> queue2 = scheduler_->NewTimerTaskQueue(
+      MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
+
+  EXPECT_EQ(queue1->GetTimeDomain(), scheduler_->real_time_domain());
+  EXPECT_EQ(queue2->GetTimeDomain(), scheduler_->real_time_domain());
+
+  scheduler_->OnShutdownTaskQueue(queue1);
+
+  scheduler_->EnableVirtualTime(
+      MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
+
+  // Virtual time should be enabled for queue2, as it is a regular queue and
+  // nothing should change for queue1 because it was shut down.
+  EXPECT_EQ(queue1->GetTimeDomain(), scheduler_->real_time_domain());
+  EXPECT_EQ(queue2->GetTimeDomain(), scheduler_->GetVirtualTimeDomain());
+}
+
 }  // namespace main_thread_scheduler_impl_unittest
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.cc
deleted file mode 100644
index 0686a1eb..0000000
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.cc
+++ /dev/null
@@ -1,45 +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.
-
-#include "third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h"
-
-#include "base/time/default_tick_clock.h"
-
-namespace blink {
-namespace scheduler {
-
-TaskCostEstimator::TaskCostEstimator(const base::TickClock* time_source,
-                                     int sample_count,
-                                     double estimation_percentile)
-    : rolling_time_delta_history_(sample_count),
-      time_source_(time_source),
-      outstanding_task_count_(0),
-      estimation_percentile_(estimation_percentile) {}
-
-TaskCostEstimator::~TaskCostEstimator() = default;
-
-void TaskCostEstimator::WillProcessTask(const base::PendingTask& pending_task) {
-  // Avoid measuring the duration in nested run loops.
-  if (++outstanding_task_count_ == 1)
-    task_start_time_ = time_source_->NowTicks();
-}
-
-void TaskCostEstimator::DidProcessTask(const base::PendingTask& pending_task) {
-  if (--outstanding_task_count_ == 0) {
-    base::TimeDelta duration = time_source_->NowTicks() - task_start_time_;
-    rolling_time_delta_history_.InsertSample(duration);
-  }
-}
-
-base::TimeDelta TaskCostEstimator::expected_task_duration() const {
-  return rolling_time_delta_history_.Percentile(estimation_percentile_);
-}
-
-void TaskCostEstimator::Clear() {
-  rolling_time_delta_history_.Clear();
-  expected_task_duration_ = base::TimeDelta();
-}
-
-}  // namespace scheduler
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h
deleted file mode 100644
index c3e54a3..0000000
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h
+++ /dev/null
@@ -1,52 +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.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_COST_ESTIMATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_COST_ESTIMATOR_H_
-
-#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/time/time.h"
-#include "cc/base/rolling_time_delta_history.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
-
-namespace base {
-class TickClock;
-}
-
-namespace blink {
-namespace scheduler {
-
-// Estimates the cost of running tasks based on historical timing data.
-class PLATFORM_EXPORT TaskCostEstimator
-    : public base::MessageLoop::TaskObserver {
- public:
-  TaskCostEstimator(const base::TickClock* time_source,
-                    int sample_count,
-                    double estimation_percentile);
-  ~TaskCostEstimator() override;
-
-  base::TimeDelta expected_task_duration() const;
-
-  // TaskObserver implementation:
-  void WillProcessTask(const base::PendingTask& pending_task) override;
-  void DidProcessTask(const base::PendingTask& pending_task) override;
-
-  void Clear();
-
- private:
-  cc::RollingTimeDeltaHistory rolling_time_delta_history_;
-  const base::TickClock* time_source_;  // NOT OWNED
-  int outstanding_task_count_;
-  double estimation_percentile_;
-  base::TimeTicks task_start_time_;
-  base::TimeDelta expected_task_duration_;
-
-  DISALLOW_COPY_AND_ASSIGN(TaskCostEstimator);
-};
-
-}  // namespace scheduler
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_COST_ESTIMATOR_H_
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
index 676c05f..03f56c56 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.cc
@@ -6,8 +6,10 @@
 
 #include <memory>
 #include "third_party/blink/public/platform/web_media_stream.h"
+#include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler.h"
+#include "third_party/blink/public/platform/web_rtc_rtp_source.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
 #include "third_party/blink/public/platform/web_rtc_session_description.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
@@ -17,20 +19,43 @@
 
 namespace {
 
-class DummyWebRTCRtpSender : public WebRTCRtpSender {
+// Having a refcounted helper class allows multiple DummyWebRTCRtpSender to
+// share the same internal states.
+class DummyRtpSenderInternal
+    : public base::RefCountedThreadSafe<DummyRtpSenderInternal> {
  private:
   static uintptr_t last_id_;
 
  public:
-  DummyWebRTCRtpSender(WebMediaStreamTrack track)
+  DummyRtpSenderInternal(WebMediaStreamTrack track)
       : id_(++last_id_), track_(std::move(track)) {}
+
+  uintptr_t id() const { return id_; }
+  WebMediaStreamTrack track() const { return track_; }
+  void set_track(WebMediaStreamTrack track) { track_ = std::move(track); }
+
+ private:
+  const uintptr_t id_;
+  WebMediaStreamTrack track_;
+};
+
+uintptr_t DummyRtpSenderInternal::last_id_ = 0;
+
+class DummyWebRTCRtpSender : public WebRTCRtpSender {
+ public:
+  DummyWebRTCRtpSender(WebMediaStreamTrack track)
+      : internal_(new DummyRtpSenderInternal(std::move(track))) {}
+  DummyWebRTCRtpSender(const DummyWebRTCRtpSender& other)
+      : internal_(other.internal_) {}
   ~DummyWebRTCRtpSender() override {}
 
+  scoped_refptr<DummyRtpSenderInternal> internal() const { return internal_; };
+
   std::unique_ptr<WebRTCRtpSender> ShallowCopy() const override {
     return nullptr;
   }
-  uintptr_t Id() const override { return id_; }
-  WebMediaStreamTrack Track() const override { return track_; }
+  uintptr_t Id() const override { return internal_->id(); }
+  WebMediaStreamTrack Track() const override { return internal_->track(); }
   WebVector<WebString> StreamIds() const override {
     return std::vector<WebString>({WebString::FromUTF8("DummyStringId")});
   }
@@ -47,37 +72,135 @@
   void GetStats(std::unique_ptr<blink::WebRTCStatsReportCallback>,
                 blink::RTCStatsFilter) override {}
 
-  void set_track(WebMediaStreamTrack track) { track_ = std::move(track); }
+ private:
+  scoped_refptr<DummyRtpSenderInternal> internal_;
+};
+
+class DummyWebRTCRtpReceiver : public WebRTCRtpReceiver {
+ private:
+  static uintptr_t last_id_;
+
+ public:
+  DummyWebRTCRtpReceiver(WebMediaStreamSource::Type type)
+      : id_(++last_id_), track_() {
+    if (type == WebMediaStreamSource::Type::kTypeAudio) {
+      WebMediaStreamSource web_source;
+      web_source.Initialize(WebString::FromUTF8("remoteAudioId"),
+                            WebMediaStreamSource::Type::kTypeAudio,
+                            WebString::FromUTF8("remoteAudioName"),
+                            true /* remote */);
+      track_.Initialize(web_source.Id(), web_source);
+    } else {
+      DCHECK_EQ(type, WebMediaStreamSource::Type::kTypeVideo);
+      WebMediaStreamSource web_source;
+      web_source.Initialize(WebString::FromUTF8("remoteVideoId"),
+                            WebMediaStreamSource::Type::kTypeVideo,
+                            WebString::FromUTF8("remoteVideoName"),
+                            true /* remote */);
+      track_.Initialize(web_source.Id(), web_source);
+    }
+  }
+  DummyWebRTCRtpReceiver(const DummyWebRTCRtpReceiver& other)
+      : id_(other.id_), track_(other.track_) {}
+  ~DummyWebRTCRtpReceiver() override {}
+
+  std::unique_ptr<WebRTCRtpReceiver> ShallowCopy() const override {
+    return nullptr;
+  }
+  uintptr_t Id() const override { return id_; }
+  const WebMediaStreamTrack& Track() const override { return track_; }
+  WebVector<WebString> StreamIds() const override {
+    return WebVector<WebString>();
+  }
+  WebVector<std::unique_ptr<WebRTCRtpSource>> GetSources() override {
+    return WebVector<std::unique_ptr<WebRTCRtpSource>>();
+  }
+  void GetStats(std::unique_ptr<blink::WebRTCStatsReportCallback>,
+                RTCStatsFilter) override {}
+  std::unique_ptr<webrtc::RtpParameters> GetParameters() const override {
+    return nullptr;
+  }
 
  private:
   const uintptr_t id_;
   WebMediaStreamTrack track_;
 };
 
-uintptr_t DummyWebRTCRtpSender::last_id_ = 0;
+uintptr_t DummyWebRTCRtpReceiver::last_id_ = 0;
 
-class DummyRTCRtpTransceiver : public WebRTCRtpTransceiver {
+// Having a refcounted helper class allows multiple DummyWebRTCRtpTransceivers
+// to share the same internal states.
+class DummyTransceiverInternal
+    : public base::RefCountedThreadSafe<DummyTransceiverInternal> {
+ private:
+  static uintptr_t last_id_;
+
  public:
-  DummyRTCRtpTransceiver(WebMediaStreamTrack track)
-      : track_(std::move(track)) {}
-  ~DummyRTCRtpTransceiver() override {}
+  DummyTransceiverInternal(WebMediaStreamSource::Type type,
+                           WebMediaStreamTrack sender_track)
+      : id_(++last_id_),
+        sender_(std::move(sender_track)),
+        receiver_(type),
+        direction_(webrtc::RtpTransceiverDirection::kSendRecv) {
+    DCHECK(sender_.Track().IsNull() ||
+           sender_.Track().Source().GetType() == type);
+  }
+
+  uintptr_t id() const { return id_; }
+  DummyWebRTCRtpSender* sender() { return &sender_; }
+  std::unique_ptr<DummyWebRTCRtpSender> Sender() const {
+    return std::make_unique<DummyWebRTCRtpSender>(sender_);
+  }
+  DummyWebRTCRtpReceiver* receiver() { return &receiver_; }
+  std::unique_ptr<DummyWebRTCRtpReceiver> Receiver() const {
+    return std::make_unique<DummyWebRTCRtpReceiver>(receiver_);
+  }
+  webrtc::RtpTransceiverDirection direction() const { return direction_; }
+  void set_direction(webrtc::RtpTransceiverDirection direction) {
+    direction_ = direction;
+  }
+
+ private:
+  const uintptr_t id_;
+  DummyWebRTCRtpSender sender_;
+  DummyWebRTCRtpReceiver receiver_;
+  webrtc::RtpTransceiverDirection direction_;
+};
+
+uintptr_t DummyTransceiverInternal::last_id_ = 0;
+
+}  // namespace
+
+class MockWebRTCPeerConnectionHandler::DummyWebRTCRtpTransceiver
+    : public WebRTCRtpTransceiver {
+ public:
+  DummyWebRTCRtpTransceiver(WebMediaStreamSource::Type type,
+                            WebMediaStreamTrack track)
+      : internal_(new DummyTransceiverInternal(type, track)) {}
+  DummyWebRTCRtpTransceiver(const DummyWebRTCRtpTransceiver& other)
+      : internal_(other.internal_) {}
+  ~DummyWebRTCRtpTransceiver() override {}
+
+  scoped_refptr<DummyTransceiverInternal> internal() const { return internal_; }
 
   WebRTCRtpTransceiverImplementationType ImplementationType() const override {
-    return WebRTCRtpTransceiverImplementationType::kPlanBSenderOnly;
+    return WebRTCRtpTransceiverImplementationType::kFullTransceiver;
   }
-  uintptr_t Id() const override { return 0u; }
+  uintptr_t Id() const override { return internal_->id(); }
   WebString Mid() const override { return WebString(); }
   std::unique_ptr<WebRTCRtpSender> Sender() const override {
-    return std::make_unique<DummyWebRTCRtpSender>(track_);
+    return internal_->Sender();
   }
   std::unique_ptr<WebRTCRtpReceiver> Receiver() const override {
-    return nullptr;
+    return internal_->Receiver();
   }
   bool Stopped() const override { return true; }
   webrtc::RtpTransceiverDirection Direction() const override {
-    return webrtc::RtpTransceiverDirection::kInactive;
+    return internal_->direction();
   }
-  void SetDirection(webrtc::RtpTransceiverDirection) override {}
+  void SetDirection(webrtc::RtpTransceiverDirection direction) override {
+    internal_->set_direction(direction);
+  }
   base::Optional<webrtc::RtpTransceiverDirection> CurrentDirection()
       const override {
     return base::nullopt;
@@ -88,11 +211,9 @@
   }
 
  private:
-  WebMediaStreamTrack track_;
+  scoped_refptr<DummyTransceiverInternal> internal_;
 };
 
-}  // namespace
-
 MockWebRTCPeerConnectionHandler::MockWebRTCPeerConnectionHandler() = default;
 
 MockWebRTCPeerConnectionHandler::~MockWebRTCPeerConnectionHandler() = default;
@@ -176,32 +297,51 @@
 MockWebRTCPeerConnectionHandler::AddTransceiverWithTrack(
     const WebMediaStreamTrack& track,
     const webrtc::RtpTransceiverInit&) {
-  std::unique_ptr<WebRTCRtpTransceiver> transceiver =
-      std::make_unique<DummyRTCRtpTransceiver>(track);
-  return transceiver;
+  transceivers_.push_back(std::unique_ptr<DummyWebRTCRtpTransceiver>(
+      new DummyWebRTCRtpTransceiver(track.Source().GetType(), track)));
+  std::unique_ptr<DummyWebRTCRtpTransceiver> copy(
+      new DummyWebRTCRtpTransceiver(*transceivers_.back()));
+  return std::unique_ptr<WebRTCRtpTransceiver>(std::move(copy));
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<WebRTCRtpTransceiver>>
 MockWebRTCPeerConnectionHandler::AddTransceiverWithKind(
     std::string kind,
     const webrtc::RtpTransceiverInit&) {
-  std::unique_ptr<WebRTCRtpTransceiver> transceiver =
-      std::make_unique<DummyRTCRtpTransceiver>(WebMediaStreamTrack());
-  return transceiver;
+  transceivers_.push_back(
+      std::unique_ptr<DummyWebRTCRtpTransceiver>(new DummyWebRTCRtpTransceiver(
+          kind == "audio" ? WebMediaStreamSource::Type::kTypeAudio
+                          : WebMediaStreamSource::Type::kTypeVideo,
+          WebMediaStreamTrack())));
+  std::unique_ptr<DummyWebRTCRtpTransceiver> copy(
+      new DummyWebRTCRtpTransceiver(*transceivers_.back()));
+  return std::unique_ptr<WebRTCRtpTransceiver>(std::move(copy));
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<WebRTCRtpTransceiver>>
 MockWebRTCPeerConnectionHandler::AddTrack(const WebMediaStreamTrack& track,
                                           const WebVector<WebMediaStream>&) {
-  std::unique_ptr<WebRTCRtpTransceiver> transceiver =
-      std::make_unique<DummyRTCRtpTransceiver>(track);
-  return transceiver;
+  transceivers_.push_back(std::unique_ptr<DummyWebRTCRtpTransceiver>(
+      new DummyWebRTCRtpTransceiver(track.Source().GetType(), track)));
+  std::unique_ptr<DummyWebRTCRtpTransceiver> copy(
+      new DummyWebRTCRtpTransceiver(*transceivers_.back()));
+  return std::unique_ptr<WebRTCRtpTransceiver>(std::move(copy));
 }
 
 webrtc::RTCErrorOr<std::unique_ptr<WebRTCRtpTransceiver>>
 MockWebRTCPeerConnectionHandler::RemoveTrack(WebRTCRtpSender* sender) {
-  static_cast<DummyWebRTCRtpSender*>(sender)->set_track(WebMediaStreamTrack());
-  return std::unique_ptr<WebRTCRtpTransceiver>(nullptr);
+  const DummyWebRTCRtpTransceiver* transceiver_of_sender = nullptr;
+  for (const auto& transceiver : transceivers_) {
+    if (transceiver->Sender()->Id() == sender->Id()) {
+      transceiver_of_sender = transceiver.get();
+      break;
+    }
+  }
+  transceiver_of_sender->internal()->sender()->internal()->set_track(
+      WebMediaStreamTrack());
+  std::unique_ptr<DummyWebRTCRtpTransceiver> copy(
+      new DummyWebRTCRtpTransceiver(*transceiver_of_sender));
+  return std::unique_ptr<WebRTCRtpTransceiver>(std::move(copy));
 }
 
 WebRTCDataChannelHandler* MockWebRTCPeerConnectionHandler::CreateDataChannel(
diff --git a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
index 164262e..2df1284 100644
--- a/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
+++ b/third_party/blink/renderer/platform/testing/testing_platform_support_with_web_rtc.h
@@ -65,6 +65,11 @@
   void Stop() override;
   WebString Id() const override;
   webrtc::PeerConnectionInterface* NativePeerConnection() override;
+
+ private:
+  class DummyWebRTCRtpTransceiver;
+
+  std::vector<std::unique_ptr<DummyWebRTCRtpTransceiver>> transceivers_;
 };
 
 class TestingPlatformSupportWithWebRTC : public TestingPlatformSupport {
diff --git a/third_party/blink/tools/debug_renderer b/third_party/blink/tools/debug_renderer
index 99dd3b96..fb29c32 100755
--- a/third_party/blink/tools/debug_renderer
+++ b/third_party/blink/tools/debug_renderer
@@ -6,7 +6,7 @@
 # Runs Chrome / content_shell and attaches to the first renderer process in gdb.
 
 RENDERER_PID_RE='Renderer \(([0-9]+)\) paused waiting for debugger'
-TARGET_FLAGS="--no-sandbox --renderer-startup-dialog"
+DEFAULT_TARGET_FLAGS=(--no-sandbox --renderer-startup-dialog)
 TARGET=$1
 shift
 
@@ -15,6 +15,19 @@
   exit 1
 fi
 
+SAW_DISABLE_FEATURES=false
+for TARGET_FLAG in "$@"; do
+  if [[ "$TARGET_FLAG" == "--disable-features="* ]]; then
+    TARGET_FLAG="$TARGET_FLAG,SpareRendererForSitePerProcess"
+    SAW_DISABLE_FEATURES=true
+  fi
+  TARGET_FLAGS+=("$TARGET_FLAG")
+done
+if [ $SAW_DISABLE_FEATURES != true ]; then
+  DEFAULT_TARGET_FLAGS+=(--disable-features=SpareRendererForSitePerProcess)
+fi
+TARGET_FLAGS=(${DEFAULT_TARGET_FLAGS[@]} ${TARGET_FLAGS[@]})
+
 if [ -z "$DEBUGGER" ]; then
   if which lldb > /dev/null; then
     DEBUGGER="lldb"
@@ -29,7 +42,8 @@
 fi
 
 OUTPUT=$(mktemp "${TMPDIR:-/tmp}"/"$(basename $0)".XXXXX)
-"$TARGET" $TARGET_FLAGS "$@" 2>&1 | tee $OUTPUT &
+echo "$TARGET" ${TARGET_FLAGS[@]} >&2
+"$TARGET" ${TARGET_FLAGS[@]} 2>&1 > >(tee $OUTPUT) &
 BROWSER_PID=$!
 
 wait_renderer_pid() {
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
index f3aac03..7d42055d 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -292,9 +292,6 @@
 Bug(none) paint/invalidation/scroll/scroll-in-transformed-layer.html [ Failure ]
 Bug(none) paint/invalidation/scroll/scroll-with-transformed-parent-layer.html [ Failure ]
 
-# Under-invalidations.
-crbug.com/644358 paint/invalidation/background/full-viewport-repaint-for-background-attachment-fixed.html [ Crash ]
-
 # Text failures due to layerization differences
 Bug(none) compositing/overflow/scroller-with-border-radius.html [ Failure ]
 Bug(none) css3/blending/mix-blend-mode-2nd-stacking-context-composited.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 99b01b0a..463c38e 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2994,7 +2994,6 @@
 crbug.com/849859 external/wpt/web-animations/timing-model/animations/pausing-an-animation.html [ Failure ]
 crbug.com/875622 external/wpt/css/css-animations/AnimationEffect-getComputedTiming.tentative.html [ Pass Failure ]
 crbug.com/875622 external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html [ Pass Failure ]
-crbug.com/875622 external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html [ Pass Failure Timeout ]
 
 crbug.com/552085 external/wpt/css/css-cascade/important-prop.html [ Failure ]
 
@@ -3138,7 +3137,6 @@
 crbug.com/626703 external/wpt/css/css-sizing/range-percent-intrinsic-size-2.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/overflow-wrap/overflow-wrap-anywhere-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/filter-effects/filter-subregion-01.html [ Failure ]
-crbug.com/626703 external/wpt/webrtc/RTCTrackEvent-fire.html [ Timeout ]
 crbug.com/915204 external/wpt/css/css-multicol/multicol-span-all-005.html [ Failure ]
 crbug.com/626703 [ Android ] external/wpt/css/css-layout-api/auto-block-size-absolute.https.html [ Failure ]
 crbug.com/626703 [ Mac10.10 ] external/wpt/css/css-layout-api/auto-block-size-absolute.https.html [ Failure ]
@@ -3170,7 +3168,6 @@
 crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
 crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-classic.html [ Timeout ]
 crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/no-active-script-manual-module.html [ Timeout ]
-crbug.com/626703 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver.https.html [ Timeout ]
 crbug.com/626703 external/wpt/html/webappapis/user-prompts/newline-normalization-manual.html [ Skip ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/html/browsers/browsing-the-web/read-media/pageload-video.html [ Crash ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html [ Crash ]
@@ -4888,8 +4885,6 @@
 crbug.com/771492 [ Mac ] external/wpt/css/css-tables/table-model-fixup-2.html [ Failure ]
 
 # Suspended WebRTC tests
-# Test fails, and output changes for each run.
-crbug.com/771540 external/wpt/webrtc/RTCPeerConnection-setLocalDescription-answer.html [ Pass Failure ]
 # Timeout due to events not firing
 crbug.com/781935 fast/mediastream/MediaStreamTrack-observer-iterate-no-crash.html [ Timeout ]
 crbug.com/781935 fast/mediastream/MediaStreamTrack.html [ Timeout ]
@@ -5184,14 +5179,6 @@
 crbug.com/826936 external/wpt/webauthn/getcredential-passing.https.html [ Pass Timeout Failure ]
 crbug.com/826936 external/wpt/webauthn/getcredential-timeout.https.html [ Pass Timeout Failure ]
 
-# WebRTC codec tests - software H.264 not present on webkit bot family
-crbug.com/840659 external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
-crbug.com/840659 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
-
-# WebRTC: In "Plan B", remote tracks are unmuted by default, so tests time out
-# waiting for "unmute".
-crbug.com/889487 external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Timeout ]
-
 # Sheriff 2018-04-11
 crbug.com/831796 fast/events/autoscroll-in-textfield.html [ Failure Pass ]
 crbug.com/831685 [ Linux ] external/wpt/2dcontext/compositing/2d.composite.canvas.lighter.html [ Pass Timeout ]
@@ -5701,9 +5688,6 @@
 crbug.com/891510 [ Win7 ] fast/dom/inline-event-attributes-release.html [ Failure Pass ]
 crbug.com/892772 [ Mac ] editing/caret/caret-painting-low-dpi.html [ Failure Pass ]
 
-# Flakiness in DTMF
-crbug.com/893112 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass Failure ]
-
 # Sheriff 2018-10-09
 crbug.com/806357 [ Linux ] virtual/threaded/fast/events/pointerevents/pinch/pointerevent_touch-action-pinch_zoom_touch.html [ Pass Failure ]
 crbug.com/893869 [ Linux ] css3/masking/mask-repeat-space-padding.html [ Failure Pass ]
@@ -5897,6 +5881,15 @@
 crbug.com/919559 [ Win ] virtual/outofblink-cors/http/tests/fetch/window/thorough/redirect-loop.html [ Timeout Pass ]
 crbug.com/919587 [ Linux ] virtual/threaded/fast/idle-callback/idle_periods.html [ Pass Failure ]
 
+# WebRTC Unified Plan
+crbug.com/920979 external/wpt/webrtc/RTCRtpTransceiver.https.html [ Timeout ]
+# WebRTC Plan B
+crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Timeout ]
+crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-fire.html [ Timeout ]
+# WebRTC codec tests - software H.264 not present on webkit bot family
+crbug.com/840659 external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
+crbug.com/840659 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
+
 # Sheriff 2019-01-11
 crbug.com/921038 virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/legacy-performance-memory-counters-enabled.html [ Skip ]
 
@@ -5934,3 +5927,4 @@
 # Sheriff 2019-01-17
 crbug.com/922955 external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Failure Pass ]
 crbug.com/922955 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Failure Pass ]
+crbug.com/923064 fast/loader/document-open-iframe-then-detach.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 2c51e12..5d233a7 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -29,6 +29,11 @@
     "base": "animations/animationworklet",
     "args": ["--enable-threaded-compositing"]
   },
+   {
+    "prefix": "threaded",
+    "base": "external/wpt/animation-worklet",
+    "args": ["--enable-threaded-compositing"]
+  },
   {
     "prefix": "threaded",
     "base": "lifecycle",
@@ -680,9 +685,9 @@
     "args": ["--enable-blink-features=OffMainThreadWorkerScriptFetch"]
   },
   {
-    "prefix": "webrtc-wpt-unified-plan",
+    "prefix": "webrtc-wpt-plan-b",
     "base": "external/wpt/webrtc",
-    "args": ["--enable-features=RTCUnifiedPlanByDefault"]
+    "args": ["--disable-features=RTCUnifiedPlanByDefault"]
   },
   {
     "prefix": "video-surface-layer",
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/animation-worklet-inside-iframe.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/animation-worklet-inside-iframe.https.html
index cad04b2..b021863 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/animation-worklet-inside-iframe.https.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/animation-worklet-inside-iframe.https.html
@@ -3,6 +3,7 @@
 <link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
 <script src="common.js"></script>
 
 <style>
@@ -47,7 +48,7 @@
       animation.play();
 
       assert_equals(data, '0.4');
-      waitTwoAnimationFrames(t.step_func_done(() => {
+      waitForAsyncAnimationFrames(1).then(t.step_func_done(() => {
         assert_equals(getComputedStyle(target).opacity, '0.5');
       }));
     });
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/common.js b/third_party/blink/web_tests/external/wpt/animation-worklet/common.js
index d029ab7..eb114f2 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/common.js
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/common.js
@@ -8,26 +8,25 @@
   `);
 }
 
+function registerConstantLocalTimeAnimator(localTime) {
+  return runInAnimationWorklet(`
+    registerAnimator('constant_time', class {
+      animate(currentTime, effect) { effect.localTime = ${localTime}; }
+    });
+  `);
+}
+
+
 function runInAnimationWorklet(code) {
   return CSS.animationWorklet.addModule(
     URL.createObjectURL(new Blob([code], {type: 'text/javascript'}))
   );
 }
 
-function waitForAnimationFrames(count, callback) {
-  function rafCallback() {
-    if (count <= 0) {
-      callback();
-    } else {
-      count -= 1;
-      window.requestAnimationFrame(rafCallback);
-    }
-  }
-  rafCallback();
-};
-
-// Wait for two main thread frames to guarantee that compositor has produced
-// at least one frame. Note that this is a Chrome-only concept.
-function waitTwoAnimationFrames(callback) {
-  waitForAnimationFrames(2, callback);
-};
+function waitForAsyncAnimationFrames(count) {
+  // In Chrome, waiting for N+1 main thread frames guarantees that compositor has produced
+  // at least N frames.
+  // TODO(majidvp): re-evaluate this choice once other browsers have implemented
+  // AnimationWorklet.
+  return waitForAnimationFrames(count + 1);
+}
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/resources/animator-iframe.html b/third_party/blink/web_tests/external/wpt/animation-worklet/resources/animator-iframe.html
index 2a8895919..e30cc28 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/resources/animator-iframe.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/resources/animator-iframe.html
@@ -6,6 +6,7 @@
   background-color: #00ff00;
 }
 </style>
+<script src="/web-animations/testcommon.js"></script>
 <script src="../common.js"></script>
 
 <script id="iframe_worklet" type="text/worklet">
@@ -32,7 +33,7 @@
   const effect = new KeyframeEffect(target, [{ opacity: 0 }], { duration: 1000 });
   const animation = new WorkletAnimation('iframe_animator', effect);
   animation.play();
-  waitTwoAnimationFrames( _ => {
+  waitForAnimationFrames(2).then(_ => {
     window.parent.postMessage(getComputedStyle(target).opacity, '*');
   });
  });
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/scroll-timeline-writing-modes.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/scroll-timeline-writing-modes.https.html
index 9cf99cb..5b20f03b 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/scroll-timeline-writing-modes.https.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/scroll-timeline-writing-modes.https.html
@@ -3,15 +3,9 @@
 <link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
 <script src="common.js"></script>
 
-<script id="worklet_code"  type="text/worklet">
-registerAnimator("test_animator", class {
-  animate(currentTime, effect) {
-    effect.localTime = currentTime;
-  }
-});
-</script>
 
 <script>
 // Creates a DOM structure like:
@@ -63,15 +57,14 @@
     timeRange: 1000,
     orientation: timeline_orientation
   });
-  const animation = new WorkletAnimation('test_animator', effect, timeline);
+  const animation = new WorkletAnimation('passthrough', effect, timeline);
   animation.play();
 }
 
 setup(setupAndRegisterTests, {explicit_done: true});
 
 function setupAndRegisterTests() {
-  const worklet_code = document.getElementById('worklet_code').textContent;
-  runInAnimationWorklet(worklet_code).then(() => {
+  registerPassthroughAnimator().then(() => {
     // Note that block horizontal-tb is tested implicitly in the basic
     // ScrollTimeline tests (as it is the default).
     async_test(
@@ -104,7 +97,7 @@
       elements.scroller.scrollWidth - elements.scroller.clientWidth;
   elements.scroller.scrollLeft = 0.25 * maxScroll;
 
-  waitForAnimationFrames(3, t.step_func_done(() => {
+  waitForAsyncAnimationFrames(2).then(t.step_func_done(() => {
     assert_equals(
         getComputedStyle(elements.box).transform, 'matrix(1, 0, 0, 1, 0, 50)');
   }));
@@ -120,7 +113,7 @@
       elements.scroller.scrollWidth - elements.scroller.clientWidth;
   elements.scroller.scrollLeft = 0.75 * maxScroll;
 
-  waitForAnimationFrames(3, t.step_func_done(() => {
+  waitForAsyncAnimationFrames(2).then(t.step_func_done(() => {
     assert_equals(
         getComputedStyle(elements.box).transform, 'matrix(1, 0, 0, 1, 0, 50)');
   }));
@@ -136,7 +129,7 @@
       elements.scroller.scrollWidth - elements.scroller.clientWidth;
   elements.scroller.scrollLeft = 0.75 * maxScroll;
 
-  waitForAnimationFrames(3, t.step_func_done(() => {
+  waitForAsyncAnimationFrames(2).then(t.step_func_done(() => {
     assert_equals(
         getComputedStyle(elements.box).transform, 'matrix(1, 0, 0, 1, 0, 50)');
   }));
@@ -151,7 +144,7 @@
       elements.scroller.scrollHeight - elements.scroller.clientHeight;
   elements.scroller.scrollTop = 0.25 * maxScroll;
 
-  waitForAnimationFrames(3, t.step_func_done(() => {
+  waitForAsyncAnimationFrames(2).then(t.step_func_done(() => {
     assert_equals(
         getComputedStyle(elements.box).transform, 'matrix(1, 0, 0, 1, 0, 50)');
   }));
@@ -167,7 +160,7 @@
       elements.scroller.scrollHeight - elements.scroller.clientHeight;
   elements.scroller.scrollTop = 0.75 * maxScroll;
 
-  waitForAnimationFrames(3, t.step_func_done(() => {
+  waitForAsyncAnimationFrames(2).then(t.step_func_done(() => {
     assert_equals(
         getComputedStyle(elements.box).transform, 'matrix(1, 0, 0, 1, 0, 50)');
   }));
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-fill-mode.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-fill-mode.https.html
index 90abe082..49fead8 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-fill-mode.https.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-fill-mode.https.html
@@ -3,6 +3,7 @@
 <link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
 <script src="common.js"></script>
 
 <style>
@@ -15,33 +16,21 @@
 
 <script>
 function CreateTest(target, effect, verify, test_name) {
-  async_test(t => {
-   runInAnimationWorklet(
-    document.getElementById('simple_animate').textContent
-   ).then(_ => {
-    const animation = new WorkletAnimation('test_animator', effect);
+  promise_test(async function() {
+    await registerConstantLocalTimeAnimator(2000);
+    const animation = new WorkletAnimation('constant_time', effect);
     animation.play();
 
-    waitTwoAnimationFrames(() => {
-      // waitTwoAnimationFrames guarantees a compositor frame that could update
-      // the opacity value in the worklet. Meanwhile, getComputedStyle needs an
-      // extra frame to fetch the updated value.
-      window.requestAnimationFrame(t.step_func_done(() => {
-        verify();
-        animation.cancel();
-      }));
-    });
-   });
+    await waitForAsyncAnimationFrames(1);
+    // waitTwoAnimationFrames guarantees a compositor frame that could update
+    // the opacity value in the worklet. Meanwhile, getComputedStyle needs an
+    // extra frame to fetch the updated value.
+    await waitForNextFrame();
+    verify();
+    animation.cancel();
   }, test_name);
 }
 </script>
-<script id="simple_animate" type="text/worklet">
-registerAnimator("test_animator", class {
-  animate(currentTime, effect) {
-    effect.localTime = 2000;
-  }
-});
-</script>
 
 <div id="target1" class='target'></div>
 <div id="target2" class='target'></div>
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-invalid-effect.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-invalid-effect.https.html
index 9a7688d3..8b72d4e 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-invalid-effect.https.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-invalid-effect.https.html
@@ -3,6 +3,7 @@
 <link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
 <script src="common.js"></script>
 
 <style>
@@ -17,8 +18,8 @@
 <script>
 'use strict';
 
-test(function() {
-  registerPassthroughAnimator();
+promise_test(async function() {
+  await registerPassthroughAnimator();
   let playFunc = function() {
     let effect = new KeyframeEffect(
         document.getElementById('target'),
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/characteristicProperties.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/characteristicProperties.https.html
index d20d435..0747af2 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/characteristicProperties.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/characteristicProperties.https.html
@@ -8,19 +8,22 @@
 'use strict';
 const test_desc = 'HeartRate device properties';
 
-bluetooth_test(() => getHealthThermometerService()
-    .then(({service}) => Promise.all([
-      service.getCharacteristic('temperature_measurement'),
-      service.getCharacteristic('measurement_interval')]))
-    .then(([temperature_measurement, measurement_interval]) => {
-      let tm_expected_properties =
-          new TestCharacteristicProperties(['indicate']);
-      assert_properties_equal(temperature_measurement.properties,
-          tm_expected_properties);
+bluetooth_test(
+    () => getHealthThermometerService()
+              .then(({service}) => Promise.all([
+                service.getCharacteristic('temperature_measurement'),
+                service.getCharacteristic('measurement_interval')
+              ]))
+              .then(([temperature_measurement, measurement_interval]) => {
+                let tm_expected_properties =
+                    new TestCharacteristicProperties(['indicate']);
+                assert_properties_equal(
+                    temperature_measurement.properties, tm_expected_properties);
 
-      let mi_expected_properties =
-          new TestCharacteristicProperties(['read', 'write', 'indicate']);
-      assert_properties_equal(measurement_interval.properties,
-          mi_expected_properties);
-    }), test_desc);
+                let mi_expected_properties = new TestCharacteristicProperties(
+                    ['read', 'write', 'indicate']);
+                assert_properties_equal(
+                    measurement_interval.properties, mi_expected_properties);
+              }),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-from-2-characteristics.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-from-2-characteristics.https.html
index aa156b2..395d49b 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-from-2-characteristics.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-from-2-characteristics.https.html
@@ -8,12 +8,14 @@
 'use strict';
 const test_desc = 'Same parent service returned from multiple characteristics.';
 
-bluetooth_test(() => getHealthThermometerService()
-    .then(({service}) => Promise.all([
-      service.getCharacteristic('measurement_interval'),
-      service.getCharacteristic('temperature_measurement')
-    ]))
-    .then(characteristics =>
-        assert_equals(characteristics[0].service, characteristics[1].service)),
+bluetooth_test(
+    () => getHealthThermometerService()
+              .then(({service}) => Promise.all([
+                service.getCharacteristic('measurement_interval'),
+                service.getCharacteristic('temperature_measurement')
+              ]))
+              .then(
+                  characteristics => assert_equals(
+                      characteristics[0].service, characteristics[1].service)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-object.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-object.https.html
index ee96121..0b2a915 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-object.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/characteristic/service-same-object.https.html
@@ -7,10 +7,11 @@
 <script>
 'use strict';
 const test_desc = '[SameObject] test for BluetoothRemoteGATTCharacteristic ' +
-  'service.';
+    'service.';
 
-bluetooth_test(() => getMeasurementIntervalCharacteristic()
-    .then(({characteristic}) =>
-        assert_equals(characteristic.service, characteristic.service)),
+bluetooth_test(
+    () => getMeasurementIntervalCharacteristic().then(
+        ({characteristic}) =>
+            assert_equals(characteristic.service, characteristic.service)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-Bluetooth.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-Bluetooth.https.html
index 2835236..9578fd6a 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-Bluetooth.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-Bluetooth.https.html
@@ -7,10 +7,12 @@
 const test_desc = 'Bluetooth IDL test';
 
 test(() => {
-  assert_throws(new TypeError(), () => new Bluetooth(),
-                'the constructor should not be callable with "new"');
-  assert_throws(new TypeError(), () => Bluetooth(),
-                'the constructor should not be callable');
+  assert_throws(
+      new TypeError(), () => new Bluetooth(),
+      'the constructor should not be callable with "new"');
+  assert_throws(
+      new TypeError(), () => Bluetooth(),
+      'the constructor should not be callable');
 
   // Bluetooth implements BluetoothDiscovery;
   assert_true('requestDevice' in navigator.bluetooth);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothDevice.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothDevice.https.html
index 631d9dd4..707ac1ab 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothDevice.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothDevice.https.html
@@ -9,23 +9,28 @@
 const test_desc_idl = 'BluetoothDevice IDL test.';
 
 test(() => {
-  assert_throws(new TypeError(), () => new BluetoothDevice(),
+  assert_throws(
+      new TypeError(), () => new BluetoothDevice(),
       'the constructor should not be callable with "new"');
-  assert_throws(new TypeError(), () => BluetoothDevice(),
+  assert_throws(
+      new TypeError(), () => BluetoothDevice(),
       'the constructor should not be callable');
 }, test_desc_idl);
 
 const test_desc_attr = 'BluetoothDevice attributes.';
 let device;
-bluetooth_test(() => getConnectedHealthThermometerDevice()
-  .then(({device}) => {
-    assert_equals(device.constructor.name, 'BluetoothDevice');
-    var old_device_id = device.id;
-    assert_throws(new TypeError(), () => device.id = 'overwritten',
-        'the device id should not be writable');
-    assert_throws(new TypeError(), () => device.name = 'overwritten',
-        'the device name should not be writable');
-    assert_equals(device.id, old_device_id);
-    assert_equals(device.name, 'Health Thermometer');
-  }), test_desc_attr);
+bluetooth_test(
+    () => getConnectedHealthThermometerDevice().then(({device}) => {
+      assert_equals(device.constructor.name, 'BluetoothDevice');
+      var old_device_id = device.id;
+      assert_throws(
+          new TypeError(), () => device.id = 'overwritten',
+          'the device id should not be writable');
+      assert_throws(
+          new TypeError(), () => device.name = 'overwritten',
+          'the device name should not be writable');
+      assert_equals(device.id, old_device_id);
+      assert_equals(device.name, 'Health Thermometer');
+    }),
+    test_desc_attr);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothUUID.html b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothUUID.html
index efebb15..2adf35a 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothUUID.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-BluetoothUUID.html
@@ -19,22 +19,25 @@
   assert_equals(BluetoothUUID.getDescriptor(NaN), base_uuid);
 }, 'NaN returns basic uuid');
 
-test(() => {
-  let max_uuid =  'ffffffff-0000-1000-8000-00805f9b34fb';
-  let nine_digits =     0xfffffffff;
-  let thirteen_digits = 0xfffffffffffff;
-  let fourteen_digits = 0xffffffffffffff;
-  assert_equals(BluetoothUUID.getService(nine_digits), max_uuid);
-  assert_equals(BluetoothUUID.getCharacteristic(nine_digits), max_uuid);
-  assert_equals(BluetoothUUID.getDescriptor(nine_digits), max_uuid);
-  assert_equals(BluetoothUUID.getService(thirteen_digits), max_uuid);
-  assert_equals(BluetoothUUID.getCharacteristic(thirteen_digits), max_uuid);
-  assert_equals(BluetoothUUID.getDescriptor(thirteen_digits), max_uuid);
-  assert_equals(BluetoothUUID.getService(fourteen_digits), base_uuid);
-  assert_equals(BluetoothUUID.getCharacteristic(fourteen_digits), base_uuid);
-  assert_equals(BluetoothUUID.getDescriptor(fourteen_digits), base_uuid);
-}, 'Values between 0xfffffffff (8 digits) and 0xffffffffffffff (14 digits)' +
-   'should return max UUID');
+test(
+    () => {
+      let max_uuid = 'ffffffff-0000-1000-8000-00805f9b34fb';
+      let nine_digits = 0xfffffffff;
+      let thirteen_digits = 0xfffffffffffff;
+      let fourteen_digits = 0xffffffffffffff;
+      assert_equals(BluetoothUUID.getService(nine_digits), max_uuid);
+      assert_equals(BluetoothUUID.getCharacteristic(nine_digits), max_uuid);
+      assert_equals(BluetoothUUID.getDescriptor(nine_digits), max_uuid);
+      assert_equals(BluetoothUUID.getService(thirteen_digits), max_uuid);
+      assert_equals(BluetoothUUID.getCharacteristic(thirteen_digits), max_uuid);
+      assert_equals(BluetoothUUID.getDescriptor(thirteen_digits), max_uuid);
+      assert_equals(BluetoothUUID.getService(fourteen_digits), base_uuid);
+      assert_equals(
+          BluetoothUUID.getCharacteristic(fourteen_digits), base_uuid);
+      assert_equals(BluetoothUUID.getDescriptor(fourteen_digits), base_uuid);
+    },
+    'Values between 0xfffffffff (8 digits) and 0xffffffffffffff (14 digits)' +
+        'should return max UUID');
 
 test(() => {
   assert_equals(BluetoothUUID.getService(Infinity), base_uuid);
@@ -54,7 +57,8 @@
   let adeadbeef_alias = 0xADEADBEEF;
   let adeadbeef_uuid = 'deadbeef-0000-1000-8000-00805f9b34fb';
   assert_equals(BluetoothUUID.getService(adeadbeef_alias), adeadbeef_uuid);
-  assert_equals(BluetoothUUID.getCharacteristic(adeadbeef_alias), adeadbeef_uuid);
+  assert_equals(
+      BluetoothUUID.getCharacteristic(adeadbeef_alias), adeadbeef_uuid);
   assert_equals(BluetoothUUID.getDescriptor(adeadbeef_alias), adeadbeef_uuid);
 }, 'Only first 32bits should be used.');
 
@@ -68,31 +72,40 @@
 test(() => {
   let all_caps_uuid = '1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D';
   assert_throws(TypeError(), () => BluetoothUUID.getService(all_caps_uuid));
-  assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(all_caps_uuid));
+  assert_throws(
+      TypeError(), () => BluetoothUUID.getCharacteristic(all_caps_uuid));
   assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(all_caps_uuid));
 }, 'A UUID String with uppercase letters is an invalid UUID.');
 
 test(() => {
   let string_alias = 'deadbeef';
   assert_throws(TypeError(), () => BluetoothUUID.getService(string_alias));
-  assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(string_alias));
+  assert_throws(
+      TypeError(), () => BluetoothUUID.getCharacteristic(string_alias));
   assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(string_alias));
 }, 'A 32bit *String* alias is invalid.');
 
 test(() => {
   let invalid_character_uuid = '0000000g-0000-1000-8000-00805f9b34fb';
-  assert_throws(TypeError(), () => BluetoothUUID.getService(invalid_character_uuid));
-  assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(invalid_character_uuid));
-  assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(invalid_character_uuid));
+  assert_throws(
+      TypeError(), () => BluetoothUUID.getService(invalid_character_uuid));
+  assert_throws(
+      TypeError(),
+      () => BluetoothUUID.getCharacteristic(invalid_character_uuid));
+  assert_throws(
+      TypeError(), () => BluetoothUUID.getDescriptor(invalid_character_uuid));
 }, 'A UUID with invalid characters is an invalid UUID.');
 
 test(() => {
-  assert_equals(BluetoothUUID.getService('alert_notification'),
-                '00001811-0000-1000-8000-00805f9b34fb');
-  assert_equals(BluetoothUUID.getCharacteristic('aerobic_heart_rate_lower_limit'),
-                '00002a7e-0000-1000-8000-00805f9b34fb');
-  assert_equals(BluetoothUUID.getDescriptor('gatt.characteristic_extended_properties'),
-                '00002900-0000-1000-8000-00805f9b34fb');
+  assert_equals(
+      BluetoothUUID.getService('alert_notification'),
+      '00001811-0000-1000-8000-00805f9b34fb');
+  assert_equals(
+      BluetoothUUID.getCharacteristic('aerobic_heart_rate_lower_limit'),
+      '00002a7e-0000-1000-8000-00805f9b34fb');
+  assert_equals(
+      BluetoothUUID.getDescriptor('gatt.characteristic_extended_properties'),
+      '00002900-0000-1000-8000-00805f9b34fb');
 }, 'A valid UUID from a name.');
 
 test(() => {
@@ -136,7 +149,8 @@
   assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(undefined));
   assert_equals(BluetoothUUID.canonicalUUID(null), base_uuid);
   assert_equals(BluetoothUUID.canonicalUUID(false), base_uuid);
-  assert_equals(BluetoothUUID.canonicalUUID(true), BluetoothUUID.canonicalUUID(1));
+  assert_equals(
+      BluetoothUUID.canonicalUUID(true), BluetoothUUID.canonicalUUID(1));
   assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(NaN));
 
   // getService
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-NavigatorBluetooth.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-NavigatorBluetooth.https.html
index b8649f11..5449c94 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-NavigatorBluetooth.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/idl/idl-NavigatorBluetooth.https.html
@@ -6,8 +6,7 @@
 const test_desc = '[SameObject] test for navigator.bluetooth';
 
 test(() => {
-  assert_true('bluetooth' in navigator,
-              'navigator.bluetooth exists.');
+  assert_true('bluetooth' in navigator, 'navigator.bluetooth exists.');
 }, 'navigator.bluetooth IDL test');
 
 test(() => {
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.html
index 033570d..7b68acf 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.html
@@ -9,8 +9,10 @@
 const test_desc = 'Device with empty name and no UUIDs nearby. Should be ' +
     'found if acceptAllDevices is true.';
 
-bluetooth_test(() => setUpPreconnectedDevice({name: ''})
-    .then(() => requestDeviceWithTrustedClick({acceptAllDevices: true}))
-    .then(device => assert_equals(device.name, '')),
+bluetooth_test(
+    () =>
+        setUpPreconnectedDevice({name: ''})
+            .then(() => requestDeviceWithTrustedClick({acceptAllDevices: true}))
+            .then(device => assert_equals(device.name, '')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.html
index d990dbf..3c2dcb7f 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/device-with-name.https.html
@@ -5,12 +5,15 @@
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
-const test_desc = 'A device with name and no UUIDs nearby. Should be found if ' +
-   'acceptAllDevices is true.';
+const test_desc =
+    'A device with name and no UUIDs nearby. Should be found if ' +
+    'acceptAllDevices is true.';
 const name = 'LE Device';
 
-bluetooth_test(() => setUpPreconnectedDevice({name: name})
-    .then(() => requestDeviceWithTrustedClick({acceptAllDevices: true}))
-    .then(device => assert_equals(device.name, name)),
+bluetooth_test(
+    () =>
+        setUpPreconnectedDevice({name: name})
+            .then(() => requestDeviceWithTrustedClick({acceptAllDevices: true}))
+            .then(device => assert_equals(device.name, name)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.html
index bd9e586..d4c2677 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.html
@@ -10,13 +10,14 @@
     'with no optionalServices. Should not get access to any services.';
 const expected = new DOMException(
     'Origin is not allowed to access any service. ' +
-    'Tip: Add the service UUID to \'optionalServices\' in ' +
-    'requestDevice() options. https://goo.gl/HxfxSQ',
+        'Tip: Add the service UUID to \'optionalServices\' in ' +
+        'requestDevice() options. https://goo.gl/HxfxSQ',
     'SecurityError');
 
-bluetooth_test(() => getConnectedHealthThermometerDevice({acceptAllDevices: true})
-    .then(({device}) => assert_promise_rejects_with_message(
-      device.gatt.getPrimaryServices(),
-      expected)),
+bluetooth_test(
+    () => getConnectedHealthThermometerDevice({acceptAllDevices: true})
+              .then(
+                  ({device}) => assert_promise_rejects_with_message(
+                      device.gatt.getPrimaryServices(), expected)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.html
index 3c0f4c11..86377c28 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.html
@@ -7,20 +7,23 @@
 <script>
 'use strict';
 const test_desc = 'requestDevice called with acceptAllDevices: true and with ' +
-   'optionalServices. Should get access to services.';
+    'optionalServices. Should get access to services.';
 
-bluetooth_test(() => getTwoHealthThermometerServicesDevice()
-    .then(() => requestDeviceWithTrustedClick({
-      acceptAllDevices: true,
-      optionalServices: ['health_thermometer']
-    }))
-    .then(device => device.gatt.connect())
-    .then(gattServer => gattServer.getPrimaryServices())
-    .then(services => {
-      assert_equals(services.length, 2);
-      services.forEach(service => {
-        assert_equals(service.uuid,
-          BluetoothUUID.getService('health_thermometer'));
-      });
-    }), test_desc);
+bluetooth_test(
+    () => getTwoHealthThermometerServicesDevice()
+              .then(() => requestDeviceWithTrustedClick({
+                      acceptAllDevices: true,
+                      optionalServices: ['health_thermometer']
+                    }))
+              .then(device => device.gatt.connect())
+              .then(gattServer => gattServer.getPrimaryServices())
+              .then(services => {
+                assert_equals(services.length, 2);
+                services.forEach(service => {
+                  assert_equals(
+                      service.uuid,
+                      BluetoothUUID.getService('health_thermometer'));
+                });
+              }),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html
index 253e311..630548d 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-filter.https.html
@@ -10,16 +10,16 @@
     'service.';
 const expected = new DOMException(
     'requestDevice() called with a filter containing a blocklisted UUID. ' +
-    'https://goo.gl/4NeimX',
+        'https://goo.gl/4NeimX',
     'SecurityError');
 
-bluetooth_test(() => setUpPreconnectedDevice({
-  knownServiceUUIDs: ['human_interface_device']
-})
-    .then(() => assert_promise_rejects_with_message(
-        requestDeviceWithTrustedClick({
-          filters: [{services: ['human_interface_device']}]
-        }),
-        expected, 'Requesting blocklisted service rejects.')),
+bluetooth_test(
+    () =>
+        setUpPreconnectedDevice({knownServiceUUIDs: ['human_interface_device']})
+            .then(
+                () => assert_promise_rejects_with_message(
+                    requestDeviceWithTrustedClick(
+                        {filters: [{services: ['human_interface_device']}]}),
+                    expected, 'Requesting blocklisted service rejects.')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.html
index 7189b643..5c176b82 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.html
@@ -8,26 +8,30 @@
 'use strict';
 const test_desc = 'Blocklisted UUID in optionalServices is removed and ' +
     'access not granted.';
-const expected = new DOMException('Origin is not allowed to access the ' +
-    'service. Tip: Add the service UUID to \'optionalServices\' in ' +
-    'requestDevice() options. https://goo.gl/HxfxSQ',
+const expected = new DOMException(
+    'Origin is not allowed to access the ' +
+        'service. Tip: Add the service UUID to \'optionalServices\' in ' +
+        'requestDevice() options. https://goo.gl/HxfxSQ',
     'SecurityError');
 let device, fake_peripheral;
 
-bluetooth_test(() => getDiscoveredHealthThermometerDevice({
-  filters: [{services: ['health_thermometer']}],
-  optionalServices: ['human_interface_device']
-})
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() =>
-        fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS}))
-    .then(() => device.gatt.connect())
-    .then(() => Promise.all([
-      assert_promise_rejects_with_message(
-        device.gatt.getPrimaryService('human_interface_device'),
-        expected, 'Blocklisted service not accessible.'),
-      assert_promise_rejects_with_message(
-        device.gatt.getPrimaryServices('human_interface_device'),
-        expected, 'Blocklisted services not accessible.')])),
+bluetooth_test(
+    () => getDiscoveredHealthThermometerDevice({
+            filters: [{services: ['health_thermometer']}],
+            optionalServices: ['human_interface_device']
+          })
+              .then(_ => ({device, fake_peripheral} = _))
+              .then(
+                  () => fake_peripheral.setNextGATTConnectionResponse(
+                      {code: HCI_SUCCESS}))
+              .then(() => device.gatt.connect())
+              .then(() => Promise.all([
+                assert_promise_rejects_with_message(
+                    device.gatt.getPrimaryService('human_interface_device'),
+                    expected, 'Blocklisted service not accessible.'),
+                assert_promise_rejects_with_message(
+                    device.gatt.getPrimaryServices('human_interface_device'),
+                    expected, 'Blocklisted services not accessible.')
+              ])),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.html
index ba8a090..5ca358a0e 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.html
@@ -10,10 +10,11 @@
 const DEVICE_NAME = 'a_device_name_that_is_longer_than_29_bytes_but_' +
     'shorter_than_248_bytes';
 
-bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{name: DEVICE_NAME}]
-    }))
-    .then(device => assert_equals(device.name, DEVICE_NAME)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: DEVICE_NAME})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{name: DEVICE_NAME}]}))
+              .then(device => assert_equals(device.name, DEVICE_NAME)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.html
index eed7b87..788d9f80 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.html
@@ -9,8 +9,8 @@
 const test_desc = 'A filter must restrict the devices in some way.';
 const expected = new TypeError();
 
-bluetooth_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick({filters: [{}]}),
-    expected),
+bluetooth_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick({filters: [{}]}), expected),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.html
index fbbd6dac..65bfc644 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.html
@@ -7,12 +7,13 @@
 <script>
 'use strict';
 const test_desc = 'An empty |filters| member should result in a TypeError';
-const expected = new DOMException('Failed to execute \'requestDevice\' on ' +
-    '\'Bluetooth\': \'filters\' member must be non-empty to find any devices.',
+const expected = new DOMException(
+    'Failed to execute \'requestDevice\' on ' +
+        '\'Bluetooth\': \'filters\' member must be non-empty to find any devices.',
     new TypeError());
 
-bluetooth_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick({filters: []}),
-    expected),
+bluetooth_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick({filters: []}), expected),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.html
index aa6c8308..549f0d4 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.html
@@ -9,36 +9,29 @@
 const test_desc = 'requestDevice with empty namePrefix. ' +
     'Should reject with TypeError.';
 const expected = new TypeError();
-const test_specs = [{
-  filters: [{ namePrefix: ''}]
-}, {
-  filters: [{ namePrefix: '', name: 'Name'}]
-}, {
-  filters: [{ namePrefix: '', services: ['heart_rate']}]
-}, {
-  filters: [{ namePrefix: '', name: 'Name', services: ['heart_rate']}]
-}, {
-  filters: [{ namePrefix: ''}],
-  optionalServices: ['heart_rate']
-}, {
-  filters: [{ namePrefix: '', name: 'Name'}],
-  optionalServices: ['heart_rate']
-}, {
-  filters: [{ namePrefix: '', services: ['heart_rate']}],
-  optionalServices: ['heart_rate']
-}, {
-  filters: [{ namePrefix: '', name: 'Name', services: ['heart_rate']}],
-  optionalServices: ['heart_rate']
-}];
+const test_specs = [
+  {filters: [{namePrefix: ''}]}, {filters: [{namePrefix: '', name: 'Name'}]},
+  {filters: [{namePrefix: '', services: ['heart_rate']}]},
+  {filters: [{namePrefix: '', name: 'Name', services: ['heart_rate']}]},
+  {filters: [{namePrefix: ''}], optionalServices: ['heart_rate']},
+  {filters: [{namePrefix: '', name: 'Name'}], optionalServices: ['heart_rate']},
+  {
+    filters: [{namePrefix: '', services: ['heart_rate']}],
+    optionalServices: ['heart_rate']
+  },
+  {
+    filters: [{namePrefix: '', name: 'Name', services: ['heart_rate']}],
+    optionalServices: ['heart_rate']
+  }
+];
 
 bluetooth_test(() => {
-    let test_promises = Promise.resolve();
-    test_specs.forEach(args => {
-      test_promises = test_promises
-          .then(() => assert_promise_rejects_with_message(
-              requestDeviceWithTrustedClick(args),
-              expected));
-    });
-    return test_promises;
-  }, test_desc);
+  let test_promises = Promise.resolve();
+  test_specs.forEach(args => {
+    test_promises = test_promises.then(
+        () => assert_promise_rejects_with_message(
+            requestDeviceWithTrustedClick(args), expected));
+  });
+  return test_promises;
+}, test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.html
index 6cb923a..b70a8a3 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.html
@@ -10,14 +10,12 @@
 const expected = new TypeError();
 
 bluetooth_test(() => {
-    let test_promises = Promise.resolve();
-    generateRequestDeviceArgsWithServices([]).forEach(args => {
-      test_promises = test_promises.then(() =>
-          assert_promise_rejects_with_message(
-              requestDeviceWithTrustedClick(args),
-              expected,
-              'Services member must contain at least one service'))
-    });
-    return test_promises;
-  }, test_desc);
+  let test_promises = Promise.resolve();
+  generateRequestDeviceArgsWithServices([]).forEach(
+      args => {test_promises = test_promises.then(
+                   () => assert_promise_rejects_with_message(
+                       requestDeviceWithTrustedClick(args), expected,
+                       'Services member must contain at least one service'))});
+  return test_promises;
+}, test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.html
index ab207329..d3f6e5c 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.html
@@ -6,28 +6,24 @@
 <script src="/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-const test_desc = "RequestDeviceOptions should have exactly one of " +
-    "'filters' or 'acceptAllDevices:true'. Reject with TypeError if not.";
+const test_desc = 'RequestDeviceOptions should have exactly one of ' +
+    '\'filters\' or \'acceptAllDevices:true\'. Reject with TypeError if not.';
 const expected = new DOMException(
-    "Failed to execute 'requestDevice' on 'Bluetooth': " +
-    "Either 'filters' should be present or " +
-    "'acceptAllDevices' should be true, but not both.",
+    'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+        'Either \'filters\' should be present or ' +
+        '\'acceptAllDevices\' should be true, but not both.',
     new TypeError());
 const test_specs = [
-  {},
-  {optionalServices: ['heart_rate']},
-  {filters: [], acceptAllDevices: true},
+  {}, {optionalServices: ['heart_rate']}, {filters: [], acceptAllDevices: true},
   {filters: [], acceptAllDevices: true, optionalServices: ['heart_rate']}
 ];
 
 bluetooth_test(() => {
-    let test_promises = Promise.resolve();
-    test_specs.forEach(args => {
-        test_promises = test_promises
-            .then(() => assert_promise_rejects_with_message(
-                requestDeviceWithTrustedClick(args),
-                expected))
-    });
-    return test_promises;
-  }, test_desc);
+  let test_promises = Promise.resolve();
+  test_specs.forEach(
+      args => {test_promises = test_promises.then(
+                   () => assert_promise_rejects_with_message(
+                       requestDeviceWithTrustedClick(args), expected))});
+  return test_promises;
+}, test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.html
index 2f2df741..5e1397d 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.html
@@ -9,15 +9,16 @@
 const test_desc = 'Unicode string with utf8 representation longer than 248 ' +
     'bytes in \'name\' must throw TypeError.';
 const expected = new DOMException(
-    "Failed to execute 'requestDevice' on 'Bluetooth': " +
-    "A device name can't be longer than 248 bytes.",
+    'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+        'A device name can\'t be longer than 248 bytes.',
     new TypeError());
 // \u2764's UTF-8 respresentation is 3 bytes long.
 // 83 chars * 3 bytes/char = 249 bytes
 const unicode_name = '\u2764'.repeat(83);
 
-bluetooth_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick({filters: [{name: unicode_name}]}),
-    expected),
+bluetooth_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick({filters: [{name: unicode_name}]}),
+        expected),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.html
index dcb6c40d..a270cac 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.html
@@ -8,14 +8,14 @@
 'use strict';
 const test_desc = 'A device name longer than 248 must reject.';
 const expected = new DOMException(
-    "Failed to execute 'requestDevice' on 'Bluetooth': A device " +
-    "name can't be longer than 248 bytes.",
+    'Failed to execute \'requestDevice\' on \'Bluetooth\': A device ' +
+        'name can\'t be longer than 248 bytes.',
     new TypeError());
 const name_too_long = 'a'.repeat(249);
 
-bluetooth_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick({filters: [{name: name_too_long}]}),
-    expected,
-    'Device name longer than 248'),
+bluetooth_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick({filters: [{name: name_too_long}]}),
+        expected, 'Device name longer than 248'),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.html
index fe1f919..05a9bd31 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.html
@@ -9,15 +9,16 @@
 const test_desc = 'Unicode string with utf8 representation longer than 248 ' +
     'bytes in \'namePrefix\' must throw NotFoundError.';
 const expected = new DOMException(
-    "Failed to execute 'requestDevice' on 'Bluetooth': " +
-    "A device name can't be longer than 248 bytes.",
+    'Failed to execute \'requestDevice\' on \'Bluetooth\': ' +
+        'A device name can\'t be longer than 248 bytes.',
     new TypeError());
 // \u2764's UTF-8 respresentation is 3 bytes long.
 // 83 chars * 3 bytes/char = 249 bytes
 const unicode_name = '\u2764'.repeat(83);
 
-bluetooth_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick({filters: [{namePrefix: unicode_name}]}),
-    expected),
+bluetooth_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick({filters: [{namePrefix: unicode_name}]}),
+        expected),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.html
index ab3a6ba..a31e4b3 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.html
@@ -8,14 +8,14 @@
 'use strict';
 const test_desc = 'A device name prefix longer than 248 must reject.';
 const expected = new DOMException(
-    "Failed to execute 'requestDevice' on 'Bluetooth': A device " +
-    "name can't be longer than 248 bytes.",
+    'Failed to execute \'requestDevice\' on \'Bluetooth\': A device ' +
+        'name can\'t be longer than 248 bytes.',
     new TypeError());
 const name_too_long = 'a'.repeat(249);
 
-bluetooth_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick({filters: [{namePrefix: name_too_long}]}),
-    expected,
-    'Device name longer than 248'),
+bluetooth_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick({filters: [{namePrefix: name_too_long}]}),
+        expected, 'Device name longer than 248'),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.html
index 40df9d0d..dca018b 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.html
@@ -11,8 +11,11 @@
 // 124 chars * 2 bytes/char = 248 bytes
 const DEVICE_NAME = '\u00A1'.repeat(124);
 
-bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
-    .then(() => requestDeviceWithTrustedClick({ filters: [{name: DEVICE_NAME}]}))
-    .then(device => assert_equals(device.name, DEVICE_NAME)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: DEVICE_NAME})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{name: DEVICE_NAME}]}))
+              .then(device => assert_equals(device.name, DEVICE_NAME)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.html
index d1759d6..7244910 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.html
@@ -9,8 +9,11 @@
 const test_desc = 'A device name of 248 bytes is valid.';
 const DEVICE_NAME = 'a'.repeat(248);
 
-bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
-    .then(() => requestDeviceWithTrustedClick({ filters: [{name: DEVICE_NAME}]}))
-    .then(device => assert_equals(device.name, DEVICE_NAME)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: DEVICE_NAME})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{name: DEVICE_NAME}]}))
+              .then(device => assert_equals(device.name, DEVICE_NAME)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.html
index 8e86844..ba4bdf9 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.html
@@ -11,10 +11,11 @@
 // 124 chars * 2 bytes/char = 248 bytes
 const DEVICE_NAME = '\u00A1'.repeat(124);
 
-bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{namePrefix: DEVICE_NAME}]
-    }))
-    .then(device => assert_equals(device.name, DEVICE_NAME)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: DEVICE_NAME})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{namePrefix: DEVICE_NAME}]}))
+              .then(device => assert_equals(device.name, DEVICE_NAME)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.html
index 01ed22e..ced3479 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.html
@@ -9,10 +9,11 @@
 const test_desc = 'A device namePrefix of 248 bytes is valid.';
 const DEVICE_NAME = 'a'.repeat(248);
 
-bluetooth_test(() => setUpPreconnectedDevice({name: DEVICE_NAME})
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{namePrefix: DEVICE_NAME}]
-    }))
-    .then(device => assert_equals(device.name, DEVICE_NAME)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: DEVICE_NAME})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{namePrefix: DEVICE_NAME}]}))
+              .then(device => assert_equals(device.name, DEVICE_NAME)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.html
index e6337a5..33f933ad 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.html
@@ -10,8 +10,8 @@
 const test_desc = 'requestDevice() requires an argument.';
 const expected = new TypeError();
 
-promise_test(() => assert_promise_rejects_with_message(
-    requestDeviceWithTrustedClick(),
-    expected),
+promise_test(
+    () => assert_promise_rejects_with_message(
+        requestDeviceWithTrustedClick(), expected),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.html
index 08038b9..787f34d5 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.html
@@ -12,10 +12,11 @@
 // 9 chars * 3 bytes/char = 27 bytes
 const valid_unicode_name = '\u2764'.repeat(9);
 
-bluetooth_test(() => setUpPreconnectedDevice({name: valid_unicode_name})
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{name: valid_unicode_name}]
-    }))
-    .then(device => assert_equals(device.name, valid_unicode_name)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: valid_unicode_name})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{name: valid_unicode_name}]}))
+              .then(device => assert_equals(device.name, valid_unicode_name)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.html
index fc444823..bafb36d71 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.html
@@ -6,16 +6,17 @@
 <script src="/bluetooth/resources/bluetooth-helpers.js"></script>
 <script>
 'use strict';
-const test_desc =  'A namePrefix containing unicode characters whose utf8 ' +
+const test_desc = 'A namePrefix containing unicode characters whose utf8 ' +
     'length is less than 30 must not throw an error.';
 // \u2764's UTF-8 representation is 3 bytes long.
 // 9 chars * 3 bytes/char = 27 bytes
 const valid_unicode_name = '\u2764'.repeat(9);
 
-bluetooth_test(() => setUpPreconnectedDevice({name: valid_unicode_name})
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{namePrefix: valid_unicode_name}]
-    }))
-    .then(device => assert_equals(device.name, valid_unicode_name)),
+bluetooth_test(
+    () => setUpPreconnectedDevice({name: valid_unicode_name})
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{namePrefix: valid_unicode_name}]}))
+              .then(device => assert_equals(device.name, valid_unicode_name)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.html
index 15cf902..e4880b6 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.html
@@ -8,37 +8,34 @@
 'use strict';
 const test_desc = 'Invalid optional service must reject the promise.';
 const expected = new TypeError();
-const test_specs = [{
-  optionalServices: ['wrong_service'],
-  filters: [{services: ['heart_rate']}]
-}, {
-  optionalServices: ['wrong_service'],
-  filters: [{ services: ['heart_rate'], name: 'Name'}]
-}, {
-  optionalServices: ['wrong_service'],
-  filters: [{ services: ['heart_rate'], namePrefix: 'Pre'}]
-}, {
-  optionalServices: ['wrong_service'],
-  filters: [{ services: ['heart_rate'], name: 'Name', namePrefix: 'Pre'}]
-}, {
-  optionalServices: ['wrong_service'],
-  filters: [{ name: 'Name'}]
-}, {
-  optionalServices: ['wrong_service'],
-  filters: [{ name: 'Name', namePrefix: 'Pre'}]
-}, {
-  optionalServices: ['wrong_service'],
-  filters: [{ namePrefix: 'Pre'}]
-}];
+const test_specs = [
+  {optionalServices: ['wrong_service'], filters: [{services: ['heart_rate']}]},
+  {
+    optionalServices: ['wrong_service'],
+    filters: [{services: ['heart_rate'], name: 'Name'}]
+  },
+  {
+    optionalServices: ['wrong_service'],
+    filters: [{services: ['heart_rate'], namePrefix: 'Pre'}]
+  },
+  {
+    optionalServices: ['wrong_service'],
+    filters: [{services: ['heart_rate'], name: 'Name', namePrefix: 'Pre'}]
+  },
+  {optionalServices: ['wrong_service'], filters: [{name: 'Name'}]}, {
+    optionalServices: ['wrong_service'],
+    filters: [{name: 'Name', namePrefix: 'Pre'}]
+  },
+  {optionalServices: ['wrong_service'], filters: [{namePrefix: 'Pre'}]}
+];
 
 bluetooth_test(() => {
-    let test_promises = Promise.resolve();
-    test_specs.forEach(args => {
-        test_promises =
-            test_promises.then(() => assert_promise_rejects_with_message(
-                requestDeviceWithTrustedClick(args),
-                expected));
-    });
-    return test_promises;
-  }, test_desc);
+  let test_promises = Promise.resolve();
+  test_specs.forEach(args => {
+    test_promises = test_promises.then(
+        () => assert_promise_rejects_with_message(
+            requestDeviceWithTrustedClick(args), expected));
+  });
+  return test_promises;
+}, test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.html
index 5d9b245..9d31b95 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.html
@@ -10,13 +10,12 @@
 const expected = new TypeError();
 
 bluetooth_test(() => {
-    let test_promises = Promise.resolve();
-    generateRequestDeviceArgsWithServices(['wrong_service']).forEach(args => {
-        test_promises = test_promises.then(() =>
-            assert_promise_rejects_with_message(
-              requestDeviceWithTrustedClick(args),
-              expected));
-    });
-    return test_promises;
-  }, test_desc);
+  let test_promises = Promise.resolve();
+  generateRequestDeviceArgsWithServices(['wrong_service']).forEach(args => {
+    test_promises = test_promises.then(
+        () => assert_promise_rejects_with_message(
+            requestDeviceWithTrustedClick(args), expected));
+  });
+  return test_promises;
+}, test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html
index e97991d..d5cf382 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/cross-origin-iframe.sub.https.html
@@ -13,26 +13,29 @@
     '/bluetooth/resources/health-thermometer-iframe.html'
 let iframe = document.createElement('iframe');
 
-bluetooth_test(() => setUpHealthThermometerDevice()
-    // 1. Load the iframe.
-    .then(() => new Promise(resolve => {
-      iframe.src = cross_origin_src;
-      document.body.appendChild(iframe);
-      iframe.addEventListener('load', resolve);
-    }))
-    // 2. Request the device from the iframe.
-    .then(() => new Promise(resolve => {
-      callWithTrustedClick(() => {
-        iframe.contentWindow.postMessage({
-          type: 'RequestDevice'
-        }, '*');
-      });
+bluetooth_test(
+    () => setUpHealthThermometerDevice()
+              // 1. Load the iframe.
+              .then(() => new Promise(resolve => {
+                      iframe.src = cross_origin_src;
+                      document.body.appendChild(iframe);
+                      iframe.addEventListener('load', resolve);
+                    }))
+              // 2. Request the device from the iframe.
+              .then(() => new Promise(resolve => {
+                      callWithTrustedClick(() => {
+                        iframe.contentWindow.postMessage(
+                            {type: 'RequestDevice'}, '*');
+                      });
 
-      window.onmessage = messageEvent => {
-        assert_equals(messageEvent.data, 'SecurityError: requestDevice() ' +
-            'called from cross-origin iframe.');
-        resolve();
-      }
-    })), test_desc);
+                      window.onmessage = messageEvent => {
+                        assert_equals(
+                            messageEvent.data,
+                            'SecurityError: requestDevice() ' +
+                                'called from cross-origin iframe.');
+                        resolve();
+                      }
+                    })),
+    test_desc);
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/discovery-succeeds.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/discovery-succeeds.https.html
index 21219444..5bd7ff1 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/discovery-succeeds.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/discovery-succeeds.https.html
@@ -8,21 +8,28 @@
 'use strict';
 const test_desc = 'Discover a device using alias, name, or UUID.';
 
-bluetooth_test(() => getConnectedHealthThermometerDevice()
-    // Chrome will always close the previous chooser in the process of handling
-    // a user gesture for the next request, so these need to be done
-    // sequentially.
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: [health_thermometer.alias]}]
-    }))
-    .then(device => assert_equals(device.constructor.name, 'BluetoothDevice'))
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: [health_thermometer.name]}]
-    }))
-    .then(device => assert_equals(device.constructor.name, 'BluetoothDevice'))
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: [health_thermometer.uuid]}]
-    }))
-    .then(device => assert_equals(device.constructor.name, 'BluetoothDevice')),
+bluetooth_test(
+    () => getConnectedHealthThermometerDevice()
+              // Chrome will always close the previous chooser in the process of
+              // handling a user gesture for the next request, so these need to
+              // be done sequentially.
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: [health_thermometer.alias]}]}))
+              .then(
+                  device =>
+                      assert_equals(device.constructor.name, 'BluetoothDevice'))
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: [health_thermometer.name]}]}))
+              .then(
+                  device =>
+                      assert_equals(device.constructor.name, 'BluetoothDevice'))
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: [health_thermometer.uuid]}]}))
+              .then(
+                  device => assert_equals(
+                      device.constructor.name, 'BluetoothDevice')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/doesnt-consume-user-gesture.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/doesnt-consume-user-gesture.https.html
index 3394a591..19ac36f 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/doesnt-consume-user-gesture.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/doesnt-consume-user-gesture.https.html
@@ -8,17 +8,21 @@
 'use strict';
 const test_desc = 'requestDevice calls do not consume user gestures.';
 
-bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
-    .then(() => callWithTrustedClick(() => {
-      let first = navigator.bluetooth.requestDevice({
-        filters: [{services: ['heart_rate']}]});
-      let second = navigator.bluetooth.requestDevice({
-        filters: [{services: ['heart_rate']}]});
-      return Promise.all([
-        first.then(device => assert_equals(
-          device.constructor.name, 'BluetoothDevice')),
-        second.then(device => assert_equals(
-          device.constructor.name, 'BluetoothDevice')),
-      ]);
-    })), test_desc);
+bluetooth_test(
+    () => setUpHealthThermometerAndHeartRateDevices().then(
+        () => callWithTrustedClick(() => {
+          let first = navigator.bluetooth.requestDevice(
+              {filters: [{services: ['heart_rate']}]});
+          let second = navigator.bluetooth.requestDevice(
+              {filters: [{services: ['heart_rate']}]});
+          return Promise.all([
+            first.then(
+                device =>
+                    assert_equals(device.constructor.name, 'BluetoothDevice')),
+            second.then(
+                device =>
+                    assert_equals(device.constructor.name, 'BluetoothDevice')),
+          ]);
+        })),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html
index 3f62d39..1aeae776 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html
@@ -11,57 +11,55 @@
 let matching_name = 'Health Thermometer';
 let matching_namePrefix = 'Health';
 
-let test_specs = [{
-  filters: [{
-    services: matching_services,
-  }]
-}, {
-  filters: [{
-    services: matching_services,
-    name: matching_name,
-  }]
-}, {
-  filters: [{
-    services: matching_services,
-    namePrefix: matching_namePrefix
-  }]
-}, {
-  filters: [{
-    name: matching_name,
-  }],
-  optionalServices: matching_services
-}, {
-  filters: [{
-    name: matching_name,
-    namePrefix: matching_namePrefix
-  }],
-  optionalServices: matching_services
-}, {
-  filters: [{
-    namePrefix: matching_namePrefix
-  }],
-  optionalServices: matching_services
-}, {
-  filters: [{
-    services: matching_services,
-    name: matching_name,
-    namePrefix: matching_namePrefix
-  }]
-}];
+let test_specs = [
+  {
+    filters: [{
+      services: matching_services,
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: matching_name,
+    }]
+  },
+  {filters: [{services: matching_services, namePrefix: matching_namePrefix}]}, {
+    filters: [{
+      name: matching_name,
+    }],
+    optionalServices: matching_services
+  },
+  {
+    filters: [{name: matching_name, namePrefix: matching_namePrefix}],
+    optionalServices: matching_services
+  },
+  {
+    filters: [{namePrefix: matching_namePrefix}],
+    optionalServices: matching_services
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: matching_name,
+      namePrefix: matching_namePrefix
+    }]
+  }
+];
 
-bluetooth_test(() => setUpHealthThermometerDevice()
-    .then(() => {
+bluetooth_test(
+    () => setUpHealthThermometerDevice().then(() => {
       let test_promises = Promise.resolve();
       test_specs.forEach(args => {
-        test_promises = test_promises
-            .then(() => requestDeviceWithTrustedClick(args))
-            .then(device => {
-              // We always have access to the services in matching_services
-              // because we include them in a filter or in optionalServices.
-              assert_equals(device.name, matching_name);
-              assert_true(device.name.startsWith(matching_namePrefix));
-            });
+        test_promises =
+            test_promises.then(() => requestDeviceWithTrustedClick(args))
+                .then(device => {
+                  // We always have access to the services in matching_services
+                  // because we include them in a filter or in optionalServices.
+                  assert_equals(device.name, matching_name);
+                  assert_true(device.name.startsWith(matching_namePrefix));
+                });
       });
       return test_promises;
-    }), test_desc);
+    }),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/le-not-supported.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/le-not-supported.https.html
index 4a375250..3da9abeb 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/le-not-supported.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/le-not-supported.https.html
@@ -7,12 +7,13 @@
 <script>
 'use strict';
 const test_desc = 'Reject with NotFoundError if Bluetooth is not supported.';
-const expected = new DOMException('Bluetooth Low Energy not available.',
-    'NotFoundError');
+const expected =
+    new DOMException('Bluetooth Low Energy not available.', 'NotFoundError');
 
-bluetooth_test(() => navigator.bluetooth.test.setLESupported(false)
-    .then(() => assert_promise_rejects_with_message(
-        requestDeviceWithTrustedClick({acceptAllDevices: true}),
-        expected, 'Bluetooth Low Energy is not supported.')),
+bluetooth_test(
+    () => navigator.bluetooth.test.setLESupported(false).then(
+        () => assert_promise_rejects_with_message(
+            requestDeviceWithTrustedClick({acceptAllDevices: true}), expected,
+            'Bluetooth Low Energy is not supported.')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.html
index ee9b9125..944befd 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.html
@@ -8,8 +8,10 @@
 'use strict';
 const test_desc = 'An empty name device can be obtained by empty name filter.'
 
-bluetooth_test(() => setUpPreconnectedDevice({name: ''})
-    .then(() => requestDeviceWithTrustedClick({filters: [{name: ''}]}))
-    .then(device => assert_equals(device.name, '')),
+bluetooth_test(
+    () =>
+        setUpPreconnectedDevice({name: ''})
+            .then(() => requestDeviceWithTrustedClick({filters: [{name: ''}]}))
+            .then(device => assert_equals(device.name, '')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/not-processing-user-gesture.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/not-processing-user-gesture.https.html
index 1781b7b..98dc0ce 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/not-processing-user-gesture.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/not-processing-user-gesture.https.html
@@ -11,9 +11,11 @@
     'Must be handling a user gesture to show a permission request.',
     'SecurityError');
 
-bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
-    .then(() => assert_promise_rejects_with_message(
-        navigator.bluetooth.requestDevice({filters:[{services:['heart_rate']}]}),
-        expected, 'User gesture is required')),
+bluetooth_test(
+    () => setUpHealthThermometerAndHeartRateDevices().then(
+        () => assert_promise_rejects_with_message(
+            navigator.bluetooth.requestDevice(
+                {filters: [{services: ['heart_rate']}]}),
+            expected, 'User gesture is required')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/radio-not-present.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/radio-not-present.https.html
index a66bcf9..929af8f 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/radio-not-present.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/radio-not-present.https.html
@@ -7,14 +7,15 @@
 <script>
 'use strict';
 const test_desc = 'Reject with NotFoundError if there is no BT radio present.';
-const expected = new DOMException('Bluetooth adapter not available.',
-    'NotFoundError');
+const expected =
+    new DOMException('Bluetooth adapter not available.', 'NotFoundError');
 
-bluetooth_test(() => navigator.bluetooth.test.simulateCentral({state: 'absent'})
-    .then(() => assert_promise_rejects_with_message(
-        requestDeviceWithTrustedClick({
-          filters: [{services: ['generic_access']}]
-        }),
-        expected, 'Bluetooth adapter is not present.')),
+bluetooth_test(
+    () => navigator.bluetooth.test.simulateCentral({state: 'absent'})
+              .then(
+                  () => assert_promise_rejects_with_message(
+                      requestDeviceWithTrustedClick(
+                          {filters: [{services: ['generic_access']}]}),
+                      expected, 'Bluetooth adapter is not present.')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-iframe.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-iframe.https.html
index 01590ea..64ad3d59 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-iframe.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-iframe.https.html
@@ -12,32 +12,36 @@
   iframes.push(document.createElement('iframe'));
 }
 
-bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
-    // 1. Load the iframes.
-    .then(() => {
-      let promises = [];
-      for (let iframe of iframes) {
-        iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
-        document.body.appendChild(iframe);
-        promises.push(new Promise(resolve =>
-            iframe.addEventListener('load', resolve)));
-      }
-      return Promise.all(promises);
-    })
-    // 2. Request the device from the iframes.
-    .then(() => new Promise(async (resolve) => {
-      let numMessages = 0;
-      window.onmessage = messageEvent => {
-        assert_equals(messageEvent.data, 'Success');
-        if (++numMessages === iframes.length) {
-          resolve();
-        }
-      }
+bluetooth_test(
+    () => setUpHealthThermometerAndHeartRateDevices()
+              // 1. Load the iframes.
+              .then(() => {
+                let promises = [];
+                for (let iframe of iframes) {
+                  iframe.src =
+                      '/bluetooth/resources/health-thermometer-iframe.html';
+                  document.body.appendChild(iframe);
+                  promises.push(new Promise(
+                      resolve => iframe.addEventListener('load', resolve)));
+                }
+                return Promise.all(promises);
+              })
+              // 2. Request the device from the iframes.
+              .then(() => new Promise(async (resolve) => {
+                      let numMessages = 0;
+                      window.onmessage =
+                          messageEvent => {
+                            assert_equals(messageEvent.data, 'Success');
+                            if (++numMessages === iframes.length) {
+                              resolve();
+                            }
+                          }
 
-      for (let iframe of iframes) {
-        await callWithTrustedClick(() => iframe.contentWindow.postMessage({
-            type: 'RequestDevice'
-        }, '*'));
-      }
-    })), test_desc);
+                      for (let iframe of iframes) {
+                        await callWithTrustedClick(
+                            () => iframe.contentWindow.postMessage(
+                                {type: 'RequestDevice'}, '*'));
+                      }
+                    })),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html
index 80798308..4c4a570 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/request-from-sandboxed-iframe.https.html
@@ -14,26 +14,28 @@
 
 let iframe = document.createElement('iframe');
 
-bluetooth_test(() => getConnectedHealthThermometerDevice()
-    // 1. Load the iframe.
-    .then(() => new Promise(resolve => {
-      iframe.sandbox.add('allow-scripts');
-      iframe.src = '/bluetooth/resources/health-thermometer-iframe.html';
-      document.body.appendChild(iframe);
-      iframe.addEventListener('load', resolve);
-    }))
-    // 2. Request the device from the iframe.
-    .then(() => new Promise(resolve => {
-      callWithTrustedClick(() => {
-        iframe.contentWindow.postMessage({
-          type: 'RequestDevice'
-        }, '*');
-      });
+bluetooth_test(
+    () => getConnectedHealthThermometerDevice()
+              // 1. Load the iframe.
+              .then(() => new Promise(resolve => {
+                      iframe.sandbox.add('allow-scripts');
+                      iframe.src =
+                          '/bluetooth/resources/health-thermometer-iframe.html';
+                      document.body.appendChild(iframe);
+                      iframe.addEventListener('load', resolve);
+                    }))
+              // 2. Request the device from the iframe.
+              .then(() => new Promise(resolve => {
+                      callWithTrustedClick(() => {
+                        iframe.contentWindow.postMessage(
+                            {type: 'RequestDevice'}, '*');
+                      });
 
-      window.onmessage = messageEvent => {
-        assert_equals(messageEvent.data, expected);
-        resolve();
-      }
-    })), test_desc);
+                      window.onmessage = messageEvent => {
+                        assert_equals(messageEvent.data, expected);
+                        resolve();
+                      }
+                    })),
+    test_desc);
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/same-device.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/same-device.https.html
index a83cf70..b96af439 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/same-device.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/same-device.https.html
@@ -10,21 +10,23 @@
 let devices = [];
 let push = device => devices.push(device);
 
-bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: [heart_rate.alias]}]
-    }))
-    .then(push)
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: [heart_rate.name]}]
-    }))
-    .then(push)
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: [heart_rate.uuid]}]
-    }))
-    .then(push)
-    .then(() => {
-      assert_equals(devices[0], devices[1]);
-      assert_equals(devices[1], devices[2]);
-    }), test_desc);
+bluetooth_test(
+    () => setUpHealthThermometerAndHeartRateDevices()
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: [heart_rate.alias]}]}))
+              .then(push)
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: [heart_rate.name]}]}))
+              .then(push)
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: [heart_rate.uuid]}]}))
+              .then(push)
+              .then(() => {
+                assert_equals(devices[0], devices[1]);
+                assert_equals(devices[1], devices[2]);
+              }),
+    test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/single-filter-single-service.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/single-filter-single-service.https.html
index 8735eb3f..9c0d621c 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/single-filter-single-service.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/single-filter-single-service.https.html
@@ -8,10 +8,11 @@
 'use strict';
 const test_desc = 'Simple filter selects matching device.';
 
-bluetooth_test(() => setUpHealthThermometerAndHeartRateDevices()
-    .then(() => requestDeviceWithTrustedClick({
-      filters: [{services: ['health_thermometer']}]
-    }))
-    .then(device => assert_equals(device.name, 'Health Thermometer')),
+bluetooth_test(
+    () => setUpHealthThermometerAndHeartRateDevices()
+              .then(
+                  () => requestDeviceWithTrustedClick(
+                      {filters: [{services: ['health_thermometer']}]}))
+              .then(device => assert_equals(device.name, 'Health Thermometer')),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/resources/health-thermometer-iframe.html b/third_party/blink/web_tests/external/wpt/bluetooth/resources/health-thermometer-iframe.html
index 5e24f62a..1545a51 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/resources/health-thermometer-iframe.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/resources/health-thermometer-iframe.html
@@ -3,24 +3,24 @@
 let device, gatt;
 
 function requestDeviceWithOptionsAndConnect(options) {
-  return navigator.bluetooth.requestDevice(options)
-      .then(device => device.gatt.connect());
+  return navigator.bluetooth.requestDevice(options).then(
+      device => device.gatt.connect());
 }
 
 window.addEventListener('message', (messageEvent) => {
   switch (messageEvent.data.type) {
     case 'RequestDevice':
-      navigator.bluetooth.requestDevice({
-        filters: [{services: ['generic_access']}]
-      })
+      navigator.bluetooth
+          .requestDevice({filters: [{services: ['generic_access']}]})
           .then(device => {
             if (device.constructor.name === 'BluetoothDevice') {
               parent.postMessage('Success', '*');
             } else {
               parent.postMessage(
                   `FAIL: requestDevice in iframe returned ${device.name}`, '*');
-            }}).catch(err => parent.postMessage(`${err.name}: ${err.message}`,
-                '*'));
+            }
+          })
+          .catch(err => parent.postMessage(`${err.name}: ${err.message}`, '*'));
       break;
     case 'RequestAndConnect':
       requestDeviceWithOptionsAndConnect(messageEvent.data.options)
@@ -28,7 +28,8 @@
             gatt = _;
             device = gatt.device;
             parent.postMessage('Connected', '*');
-          }).catch(err => {
+          })
+          .catch(err => {
             parent.postMessage(`FAIL: ${err}`, '*');
           });
       break;
@@ -50,8 +51,8 @@
           .catch(err => parent.postMessage(`FAIL: ${err}`, '*'));
       break;
     default:
-      parent.postMessage(`FAIL: Bad message type: ${messageEvent.data.type}`,
-          '*');
+      parent.postMessage(
+          `FAIL: Bad message type: ${messageEvent.data.type}`, '*');
   }
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html
index cfd4bc5..6362496 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/server/device-same-object.https.html
@@ -9,11 +9,13 @@
 const test_desc = '[SameObject] test for BluetoothRemoteGATTServer\'s device.';
 let device, fake_peripheral;
 
-bluetooth_test(() => getDiscoveredHealthThermometerDevice()
-    .then(_ => ({device, fake_peripheral} = _))
-    .then(() =>
-      fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS}))
-    .then(() => device.gatt.connect())
-    .then(gatt => assert_equals(gatt.device, gatt.device)),
+bluetooth_test(
+    () => getDiscoveredHealthThermometerDevice()
+              .then(_ => ({device, fake_peripheral} = _))
+              .then(
+                  () => fake_peripheral.setNextGATTConnectionResponse(
+                      {code: HCI_SUCCESS}))
+              .then(() => device.gatt.connect())
+              .then(gatt => assert_equals(gatt.device, gatt.device)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-from-2-services.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-from-2-services.https.html
index 3665493..0d5ab64 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-from-2-services.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-from-2-services.https.html
@@ -8,11 +8,14 @@
 'use strict';
 const test_desc = 'Same parent device returned from multiple services.';
 
-bluetooth_test(() => getTwoHealthThermometerServicesDevice({
-  filters: [{services: ['health_thermometer']}]
-})
-    .then(({device}) => device.gatt.getPrimaryServices('health_thermometer'))
-    .then(([service1, service2]) =>
-        assert_equals(service1.device, service2.device)),
+bluetooth_test(
+    () => getTwoHealthThermometerServicesDevice(
+              {filters: [{services: ['health_thermometer']}]})
+              .then(
+                  ({device}) =>
+                      device.gatt.getPrimaryServices('health_thermometer'))
+              .then(
+                  ([service1, service2]) =>
+                      assert_equals(service1.device, service2.device)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-object.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-object.https.html
index f43e9f02..de57c2e 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-object.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/service/device-same-object.https.html
@@ -8,10 +8,12 @@
 'use strict';
 const test_desc = '[SameObject] test for BluetoothRemoteGATTService device.';
 
-bluetooth_test(() => getHealthThermometerDevice({
-  filters: [{services: ['health_thermometer']}]
-})
-    .then(({device}) => device.gatt.getPrimaryService('health_thermometer'))
-    .then(service => assert_equals(service.device, service.device)),
+bluetooth_test(
+    () => getHealthThermometerDevice(
+              {filters: [{services: ['health_thermometer']}]})
+              .then(
+                  ({device}) =>
+                      device.gatt.getPrimaryService('health_thermometer'))
+              .then(service => assert_equals(service.device, service.device)),
     test_desc);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt
index 72b5b78..d1372c9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 82 tests; 64 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 84 tests; 66 PASS, 18 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS font-weight(valid): 'normal' keyword: normal
 PASS font-weight(valid): 'bold' keyword: bold
 FAIL font-weight(invalid): 'lighter' keyword iside @font-face: lighter assert_equals: No properties should be set. expected "" but got "lighter"
@@ -68,6 +68,8 @@
 FAIL font-style(valid): 'oblique' followed by default 20deg angle: oblique 20deg assert_equals: Unexpected resulting value. expected "oblique" but got "oblique 20deg"
 PASS font-style(valid): 'oblique' followed by maxumum 90 degree angle: oblique 90deg
 PASS font-style(valid): 'oblique' followed by minimum -90 degree angle: oblique -90deg
+PASS font-style(valid): 'oblique' followed by calc with out of range value (should be clamped): oblique calc(91deg)
+PASS font-style(valid): 'oblique' followed by calc with out of range value (should be clamped): oblique calc(-91deg)
 FAIL font-style(valid): 'oblique' followed by  angle in radians: oblique 0rad assert_equals: Unexpected resulting value. expected "oblique 0deg" but got "oblique 0rad"
 PASS font-style(invalid): 'oblique' followed by unit-less number: oblique 20
 PASS font-style(invalid): 'oblique' followed by non-angle: oblique 20px
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html
index 942686c4..cd1c22b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/at-font-face-descriptors.html
@@ -147,6 +147,8 @@
             { value: "oblique 20deg",           isValid: true,  expectedValue: "oblique", description: "'oblique' followed by default 20deg angle" },
             { value: "oblique 90deg",           isValid: true,  description: "'oblique' followed by maxumum 90 degree angle" },
             { value: "oblique -90deg",          isValid: true,  description: "'oblique' followed by minimum -90 degree angle" },
+            { value: "oblique calc(91deg)",     isValid: true,  expectedValue: "oblique 90deg", description: "'oblique' followed by calc with out of range value (should be clamped)" },
+            { value: "oblique calc(-91deg)",    isValid: true,  expectedValue: "oblique -90deg", description: "'oblique' followed by calc with out of range value (should be clamped)" },
             { value: "oblique 0rad",            isValid: true,  expectedValue: "oblique 0deg", description: "'oblique' followed by  angle in radians" },
             { value: "oblique 20",              isValid: false, description: "'oblique' followed by unit-less number" },
             { value: "oblique 20px",            isValid: false, description: "'oblique' followed by non-angle" },
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt
index 2e86c0be..5e8d789 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-parse-numeric-stretch-style-weight-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 81 tests; 77 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 81 tests; 78 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Valid value bold for font property weight used for styling.
 PASS Valid value 700 for font property weight used for styling.
 PASS Valid value 900 for font property weight used for styling.
@@ -19,7 +19,7 @@
 PASS Valid value oblique 50deg for font property style used for styling.
 PASS Valid value oblique -90deg for font property style used for styling.
 PASS Valid value oblique 90deg for font property style used for styling.
-FAIL Valid value oblique calc(90deg + 20deg) for font property style used for styling. assert_true: expected true got false
+PASS Valid value oblique calc(90deg + 20deg) for font property style used for styling.
 PASS Valid value oblique calc(30deg + 20deg) for font property style used for styling.
 PASS Invalid value 100 400 for font property weight used for styling.
 PASS Invalid value 100% 110% for font property stretch used for styling.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing-expected.txt
index 50ea1204d..2f70d3b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing-expected.txt
@@ -1,30 +1,46 @@
 This is a testharness.js-based test.
-PASS Font-style: 'italic' is valid
-PASS Font-style: 'italic' followed by angle is invalid
-PASS Font-style: 'italic' followed by non-number is invalid
-PASS Font-style: 'oblique' is valid
-PASS Font-style: 'oblique' followed by zero degrees is valid
-PASS Font-style: 'oblique' followed by positive angle in degrees is valid
-PASS Font-style: 'oblique' followed by positive angle in radians is valid
-PASS Font-style: 'oblique' followed by positive angle in gradians is valid
-PASS Font-style: 'oblique' followed by positive angle in turns is valid
-PASS Font-style: 'oblique' followed by number with invalid unit type is in valid
-PASS Font-style: 'oblique' followed by negative angle is valid
-PASS Font-style: 'oblique' followed by fractional angle is valid
-PASS Font-style: 'oblique' followed by maxumum 90 degree angle is valid
-PASS Font-style: 'oblique' followed by minimum -90 degree angle is valid
-PASS Font-style: 'oblique' followed by positive out of range angle is in invalid
-PASS Font-style: 'oblique' followed by negative out of range angle is in invalid
-PASS Font-style: 'oblique' followed by unit-less value is invalid
-PASS Font-style: 'oblique' followed by positive angle is valid
-PASS Font-style: 'oblique' followed by non-number is invalid
-PASS Font-style: 'oblique' and angle followed by non-number is invalid
-PASS Font-style: 'oblique' followed by isolated minus is invalid
-PASS Font-style: 'oblique' followed by minus and angle separated by space is invalid
-PASS Font-style: 'oblique' followed by minus and non-number is invalid
-PASS Font-style: 'oblique' followed by calc is valid
-FAIL Font-style: 'oblique' followed by calc is valid even if it must be clamped (no computation) assert_equals: Font-style: 'oblique' followed by calc is valid even if it must be clamped (no computation) expected true but got false
-FAIL Font-style: 'oblique' followed by calc is valid even if it must be clamped (with computation) assert_equals: Font-style: 'oblique' followed by calc is valid even if it must be clamped (with computation) expected true but got false
-PASS Font-style: 'oblique' followed by calc is valid even if it mixes units (with computation)
+PASS Font-style (supports): 'italic' is valid
+PASS Font-style (supports): 'italic' followed by angle is invalid
+PASS Font-style (supports): 'italic' followed by non-number is invalid
+PASS Font-style (supports): 'oblique' is valid
+PASS Font-style (supports): 'oblique' followed by zero degrees is valid
+PASS Font-style (supports): 'oblique' followed by positive angle in degrees is valid
+PASS Font-style (supports): 'oblique' followed by positive angle in radians is valid
+PASS Font-style (supports): 'oblique' followed by positive angle in gradians is valid
+PASS Font-style (supports): 'oblique' followed by positive angle in turns is valid
+PASS Font-style (supports): 'oblique' followed by number with invalid unit type is in valid
+PASS Font-style (supports): 'oblique' followed by negative angle is valid
+PASS Font-style (supports): 'oblique' followed by fractional angle is valid
+PASS Font-style (supports): 'oblique' followed by maxumum 90 degree angle is valid
+PASS Font-style (supports): 'oblique' followed by minimum -90 degree angle is valid
+PASS Font-style (supports): 'oblique' followed by positive out of range angle is in invalid
+PASS Font-style (supports): 'oblique' followed by negative out of range angle is in invalid
+PASS Font-style (supports): 'oblique' followed by unit-less value is invalid
+PASS Font-style (supports): 'oblique' followed by positive angle is valid
+PASS Font-style (supports): 'oblique' followed by non-number is invalid
+PASS Font-style (supports): 'oblique' and angle followed by non-number is invalid
+PASS Font-style (supports): 'oblique' followed by isolated minus is invalid
+PASS Font-style (supports): 'oblique' followed by minus and angle separated by space is invalid
+PASS Font-style (supports): 'oblique' followed by minus and non-number is invalid
+PASS Font-style (supports): 'oblique' followed by calc is valid
+PASS Font-style (supports): 'oblique' followed by calc is valid even if it must be clamped (no computation)
+PASS Font-style (supports): 'oblique' followed by calc is valid even if it must be clamped (with computation)
+PASS Font-style (supports): 'oblique' followed by calc is valid even if it mixes units (with computation)
+PASS Font-style (computed): 'italic' is valid
+FAIL Font-style (computed): 'oblique' is valid assert_equals: Font-style computed style: 'oblique' is valid expected "oblique" but got "italic"
+FAIL Font-style (computed): 'oblique' followed by zero degrees is valid assert_equals: Font-style computed style: 'oblique' followed by zero degrees is valid expected "oblique 0deg" but got "normal"
+FAIL Font-style (computed): 'oblique' followed by positive angle in degrees is valid assert_equals: Font-style computed style: 'oblique' followed by positive angle in degrees is valid expected "oblique 20deg" but got "italic"
+FAIL Font-style (computed): 'oblique' followed by positive angle in radians is valid assert_equals: Font-style computed style: 'oblique' followed by positive angle in radians is valid expected "oblique 28.6479deg" but got "oblique 0.5deg"
+FAIL Font-style (computed): 'oblique' followed by positive angle in gradians is valid assert_equals: Font-style computed style: 'oblique' followed by positive angle in gradians is valid expected "oblique 18deg" but got "italic"
+FAIL Font-style (computed): 'oblique' followed by positive angle in turns is valid assert_equals: Font-style computed style: 'oblique' followed by positive angle in turns is valid expected "oblique 36deg" but got "normal"
+PASS Font-style (computed): 'oblique' followed by negative angle is valid
+PASS Font-style (computed): 'oblique' followed by fractional angle is valid
+PASS Font-style (computed): 'oblique' followed by maxumum 90 degree angle is valid
+PASS Font-style (computed): 'oblique' followed by minimum -90 degree angle is valid
+PASS Font-style (computed): 'oblique' followed by positive angle is valid
+PASS Font-style (computed): 'oblique' followed by calc is valid
+PASS Font-style (computed): 'oblique' followed by calc is valid even if it must be clamped (no computation)
+PASS Font-style (computed): 'oblique' followed by calc is valid even if it must be clamped (with computation)
+PASS Font-style (computed): 'oblique' followed by calc is valid even if it mixes units (with computation)
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing.html
index 0973d63..cb5e42ea 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/variations/font-style-parsing.html
@@ -7,43 +7,54 @@
     <script src="/resources/testharnessreport.js"></script>
 </head>
 <body>
-
+    <div id="test"></div>
     <script>
-
         var testFontStyle = [
-            { style: "italic",            expectedResult: true,  message: "'italic' is valid" },
-            { style: "italic 20deg",      expectedResult: false, message: "'italic' followed by angle is invalid" },
-            { style: "italic a",          expectedResult: false, message: "'italic' followed by non-number is invalid" },
-            { style: "oblique",           expectedResult: true,  message: "'oblique' is valid" },
-            { style: "oblique 0deg",      expectedResult: true,  message: "'oblique' followed by zero degrees is valid" },
-            { style: "oblique 20deg",     expectedResult: true,  message: "'oblique' followed by positive angle in degrees is valid" },
-            { style: "oblique 0.5rad",    expectedResult: true,  message: "'oblique' followed by positive angle in radians is valid" },
-            { style: "oblique 20grad",    expectedResult: true,  message: "'oblique' followed by positive angle in gradians is valid" },
-            { style: "oblique 0.1turn",   expectedResult: true,  message: "'oblique' followed by positive angle in turns is valid" },
-            { style: "oblique 20px",      expectedResult: false, message: "'oblique' followed by number with invalid unit type is in valid" },
-            { style: "oblique -20deg",    expectedResult: true,  message: "'oblique' followed by negative angle is valid" },
-            { style: "oblique 20.1deg",   expectedResult: true,  message: "'oblique' followed by fractional angle is valid" },
-            { style: "oblique 90deg",     expectedResult: true,  message: "'oblique' followed by maxumum 90 degree angle is valid" },
-            { style: "oblique -90deg",    expectedResult: true,  message: "'oblique' followed by minimum -90 degree angle is valid" },
-            { style: "oblique 90.01deg",  expectedResult: false, message: "'oblique' followed by positive out of range angle is in invalid" },
-            { style: "oblique -90.01deg", expectedResult: false, message: "'oblique' followed by negative out of range angle is in invalid" },
-            { style: "oblique 10",        expectedResult: false, message: "'oblique' followed by unit-less value is invalid" },
-            { style: "oblique 20deg ",    expectedResult: true,  message: "'oblique' followed by positive angle is valid" },
-            { style: "oblique a",         expectedResult: false, message: "'oblique' followed by non-number is invalid" },
-            { style: "oblique 20deg a",   expectedResult: false, message: "'oblique' and angle followed by non-number is invalid" },
-            { style: "oblique -",         expectedResult: false, message: "'oblique' followed by isolated minus is invalid" },
-            { style: "oblique - 20deg",   expectedResult: false, message: "'oblique' followed by minus and angle separated by space is invalid" },
-            { style: "oblique -a",        expectedResult: false, message: "'oblique' followed by minus and non-number is invalid" },
-            { style: "oblique calc(50deg)",     expectedResult: true,  message: "'oblique' followed by calc is valid" },
-            { style: "oblique calc(-120deg)",   expectedResult: true,  message: "'oblique' followed by calc is valid even if it must be clamped (no computation)" },
-            { style: "oblique calc(6 * 20deg)", expectedResult: true,  message: "'oblique' followed by calc is valid even if it must be clamped (with computation)" },
-            { style: "oblique calc(0.1rad + 1deg)", expectedResult: true,  message: "'oblique' followed by calc is valid even if it mixes units (with computation)" }
+            { style: "italic",                      expectedResult: true,   message: "'italic' is valid" },
+            { style: "italic 20deg",                expectedResult: false,  message: "'italic' followed by angle is invalid" },
+            { style: "italic a",                    expectedResult: false,  message: "'italic' followed by non-number is invalid" },
+            { style: "oblique",                     expectedResult: true,   message: "'oblique' is valid" },
+            { style: "oblique 0deg",                expectedResult: true,   message: "'oblique' followed by zero degrees is valid" },
+            { style: "oblique 20deg",               expectedResult: true,   message: "'oblique' followed by positive angle in degrees is valid" },
+            { style: "oblique 0.5rad",              expectedResult: true,   message: "'oblique' followed by positive angle in radians is valid",    expectedValue: "oblique 28.6479deg" },
+            { style: "oblique 20grad",              expectedResult: true,   message: "'oblique' followed by positive angle in gradians is valid",   expectedValue: "oblique 18deg" },
+            { style: "oblique 0.1turn",             expectedResult: true,   message: "'oblique' followed by positive angle in turns is valid",      expectedValue: "oblique 36deg" },
+            { style: "oblique 20px",                expectedResult: false,  message: "'oblique' followed by number with invalid unit type is in valid" },
+            { style: "oblique -20deg",              expectedResult: true,   message: "'oblique' followed by negative angle is valid" },
+            { style: "oblique 20.5deg",             expectedResult: true,   message: "'oblique' followed by fractional angle is valid" },
+            { style: "oblique 90deg",               expectedResult: true,   message: "'oblique' followed by maxumum 90 degree angle is valid" },
+            { style: "oblique -90deg",              expectedResult: true,   message: "'oblique' followed by minimum -90 degree angle is valid" },
+            { style: "oblique 90.01deg",            expectedResult: false,  message: "'oblique' followed by positive out of range angle is in invalid" },
+            { style: "oblique -90.01deg",           expectedResult: false,  message: "'oblique' followed by negative out of range angle is in invalid" },
+            { style: "oblique 10",                  expectedResult: false,  message: "'oblique' followed by unit-less value is invalid" },
+            { style: "oblique 30deg",               expectedResult: true,   message: "'oblique' followed by positive angle is valid" },
+            { style: "oblique a",                   expectedResult: false,  message: "'oblique' followed by non-number is invalid" },
+            { style: "oblique 20deg a",             expectedResult: false,  message: "'oblique' and angle followed by non-number is invalid" },
+            { style: "oblique -",                   expectedResult: false,  message: "'oblique' followed by isolated minus is invalid" },
+            { style: "oblique - 20deg",             expectedResult: false,  message: "'oblique' followed by minus and angle separated by space is invalid" },
+            { style: "oblique -a",                  expectedResult: false,  message: "'oblique' followed by minus and non-number is invalid" },
+            { style: "oblique calc(50deg)",         expectedResult: true,   message: "'oblique' followed by calc is valid",                         expectedValue: "oblique 50deg" },
+            { style: "oblique calc(-120deg)",       expectedResult: true,   message: "'oblique' followed by calc is valid even if it must be clamped (no computation)",     expectedValue: "oblique -90deg" },
+            { style: "oblique calc(6 * 20deg)",     expectedResult: true,   message: "'oblique' followed by calc is valid even if it must be clamped (with computation)",   expectedValue: "oblique 90deg" },
+            { style: "oblique calc(10grad + 5deg)", expectedResult: true,   message: "'oblique' followed by calc is valid even if it mixes units (with computation)",       expectedValue: "oblique 14deg" }
         ];
 
         testFontStyle.forEach(function (testCase) {
-            test(() => { assert_equals(window.CSS.supports("font-style", testCase.style), testCase.expectedResult, "Font-style: " + testCase.message); }, "Font-style: " + testCase.message);
+            test(() => {
+                assert_equals(window.CSS.supports("font-style", testCase.style), testCase.expectedResult, "Font-style supports: " + testCase.message);
+            }, "Font-style (supports): " + testCase.message);
         });
 
+        testFontStyle.forEach(function (testCase) {
+            if (testCase.expectedResult) {
+                test(() => {
+                    let element = document.getElementById("test");
+                    element.style = "font-style: " + testCase.style;
+                    let expectedValue = (testCase.expectedValue) ? testCase.expectedValue : testCase.style;
+                    assert_equals(window.getComputedStyle(element).fontStyle, expectedValue, "Font-style computed style: " + testCase.message);
+                }, "Font-style (computed): " + testCase.message);
+            }
+        });
     </script>
 </body>
-</html>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-placeholder-ref.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-placeholder-ref.html
new file mode 100644
index 0000000..11943c2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-placeholder-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Interaction with placeholder</title>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+  </head>
+  <body>
+    <style>
+      #placeholder-i::placeholder { color: green; }
+    </style>
+    <div>
+      The following text should be green:
+      <input id="placeholder-i" part="placeholder-p" placeholder="this text"></input>
+    </div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-placeholder.html b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-placeholder.html
new file mode 100644
index 0000000..a064789
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-shadow-parts/interaction-with-placeholder.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Shadow Parts - Interaction with placeholder</title>
+    <!-- This reftest exists because getComputedStyle for
+      ::placeholder is not implemented everywhere --!>
+    <meta href="mailto:fergal@chromium.org" rel="author" title="Fergal Daly">
+    <link href="http://www.google.com/" rel="author" title="Google">
+    <link href="https://drafts.csswg.org/css-shadow-parts/" rel="help">
+    <link href="interaction-with-placeholder-ref.html" rel="match">
+    <script src="support/shadow-helper.js"></script>
+  </head>
+  <body>
+    <style>
+      #c-e::part(placeholder-p)::placeholder { color: green; }
+    </style>
+    <script>installCustomElement("custom-element", "custom-element-template");</script>
+    <template id="custom-element-template">
+      <style>
+        #placeholder-i::placeholder { color: red; }
+      </style>
+      <div>
+        The following text should be green:
+        <input id="placeholder-i" part="placeholder-p" placeholder="this text"></input>
+      </div>
+    </template>
+    <custom-element id="c-e"></custom-element>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
index f03a6e2..5bd1585 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
@@ -1,9 +1,9 @@
 This is a testharness.js-based test.
 PASS insertDTMF() should succeed if tones contains valid DTMF characters
 PASS insertDTMF() should throw InvalidCharacterError if tones contains invalid DTMF characters
-FAIL insertDTMF() should throw InvalidStateError if transceiver is stopped Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL insertDTMF() should throw InvalidStateError if transceiver.currentDirection is recvonly promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL insertDTMF() should throw InvalidStateError if transceiver.currentDirection is inactive promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL insertDTMF() should throw InvalidStateError if transceiver is stopped transceiver.stop is not a function
+PASS insertDTMF() should throw InvalidStateError if transceiver.currentDirection is recvonly
+PASS insertDTMF() should throw InvalidStateError if transceiver.currentDirection is inactive
 PASS insertDTMF() should set toneBuffer to provided tones normalized, with old tones overridden
 PASS insertDTMF() after remove and close should reject
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
index f2bcb704..622e37be 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
@@ -1,15 +1,16 @@
 This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: transceiver.stop is not a function
 PASS insertDTMF() with default duration and intertoneGap should fire tonechange events at the expected time
 PASS insertDTMF() with explicit duration and intertoneGap should fire tonechange events at the expected time
 PASS insertDTMF('') should not fire any tonechange event, including for '' tone
 PASS insertDTMF() with duration less than 40 should be clamped to 40
 PASS insertDTMF() with interToneGap less than 30 should be clamped to 30
 PASS insertDTMF with comma should delay next tonechange event for a constant 2000ms
-FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_unreached: Unexpected promise rejection: Error: assert_equals: Expect there to be only one tranceiver in pc expected 1 but got 0 Reached unreachable code
+FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_greater_than: More tonechange event is fired than expected expected a number greater than 0 but got 0
 PASS Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones
 PASS Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones
 PASS Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing
-FAIL Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing Failed to execute 'insertDTMF' on 'RTCDTMFSender': The 'canInsertDTMF' attribute is false: this sender cannot send DTMF.
 PASS Tone change event constructor works
 PASS Tone change event with unexpected name should not crash
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
index e83cef9..4294876f 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL DTLS transport goes to connected state promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
-FAIL close() causes the local transport to close immediately promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
-FAIL close() causes the other end's DTLS transport to close promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+PASS DTLS transport goes to connected state
+FAIL close() causes the local transport to close immediately assert_equals: expected "closed" but got "connected"
+PASS close() causes the other end's DTLS transport to close
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
index 99faa3c4..c12b32d6 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
@@ -1,25 +1,25 @@
 This is a testharness.js-based test.
-FAIL addTransceiver() with string argument as invalid kind should throw TypeError assert_throws: function "() => pc.addTransceiver('invalid')" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument." ("InvalidStateError") expected object "TypeError" ("TypeError")
-FAIL addTransceiver('audio') should return an audio transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL addTransceiver('video') should return a video transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL addTransceiver() with direction sendonly should have result transceiver.direction be the same Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL addTransceiver() with direction inactive should have result transceiver.direction be the same Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+PASS addTransceiver() with string argument as invalid kind should throw TypeError
+PASS addTransceiver('audio') should return an audio transceiver
+PASS addTransceiver('video') should return a video transceiver
+PASS addTransceiver() with direction sendonly should have result transceiver.direction be the same
+PASS addTransceiver() with direction inactive should have result transceiver.direction be the same
 PASS addTransceiver() with invalid direction should throw TypeError
-FAIL addTransceiver(track) should have result with sender.track be given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL addTransceiver(track) multiple times should create multiple transceivers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS addTransceiver(track) should have result with sender.track be given track
+PASS addTransceiver(track) multiple times should create multiple transceivers
 FAIL addTransceiver() with rid containing invalid non-alphanumeric characters should throw TypeError assert_throws: function "() =>
       pc.addTransceiver('audio', {
         sendEncodings: [{
           rid: '@Invalid!'
         }]
-      })" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument." ("InvalidStateError") expected object "TypeError" ("TypeError")
+      })" did not throw
 FAIL addTransceiver() with rid longer than 16 characters should throw TypeError assert_throws: function "() =>
       pc.addTransceiver('audio', {
         sendEncodings: [{
           rid: 'a'.repeat(17)
         }]
-      })" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument." ("InvalidStateError") expected object "TypeError" ("TypeError")
-FAIL addTransceiver() with valid rid value should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL addTransceiver() with valid sendEncodings should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+      })" did not throw
+PASS addTransceiver() with valid rid value should succeed
+PASS addTransceiver() with valid sendEncodings should succeed
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
index 3f7a47d..3ee7bf7 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
@@ -2,6 +2,6 @@
 FAIL createOffer() with no argument from newly created RTCPeerConnection should succeed assert_false: Expect offer to not be instance of RTCSessionDescription expected false got true
 PASS createOffer() and then setLocalDescription() should succeed
 PASS createOffer() after connection is closed should reject with InvalidStateError
-FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream assert_equals: Expect m=audio line to be found in offer SDP expected 1 but got 0
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getIdentityAssertion-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getIdentityAssertion-expected.txt
deleted file mode 100644
index f390208..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getIdentityAssertion-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-FAIL getIdentityAssertion() should load IdP proxy and return assertion generated pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should succeed if mock-idp.js return different domain and protocol in assertion pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with RTCError('idp-execution-failure') if mock-idp.js throws error assert_equals: Expect initial pc.idpErrorInfo to be null expected (object) null but got (undefined) undefined
-FAIL getIdentityAssertion() should reject with RTCError('idp-bad-script-failure') if IdP proxy script do not register its callback pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with OperationError if mock-idp.js return invalid result pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with RTCError('idp-load-failure') if IdP cannot be loaded pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with RTCError('idp-need-login') when mock-idp.js requires login assert_equals: Expect initial pc.idpLoginUrl to be null expected (object) null but got (undefined) undefined
-FAIL setIdentityProvider() with no peerIdentity provided should use peerIdentity value from getConfiguration() pc.setIdentityProvider is not a function
-FAIL Calling setIdentityProvider() multiple times should reset identity assertions pc.setIdentityProvider is not a function
-FAIL createOffer() should return SDP containing identity assertion string if identity provider is set pc.setIdentityProvider is not a function
-FAIL createOffer() should reject with NotReadableError if identitity assertion request fails pc.setIdentityProvider is not a function
-FAIL createAnswer() should reject with NotReadableError if identitity assertion request fails pc.setIdentityProvider is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getIdentityAssertion.sub-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getIdentityAssertion.sub-expected.txt
deleted file mode 100644
index f390208..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getIdentityAssertion.sub-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-FAIL getIdentityAssertion() should load IdP proxy and return assertion generated pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should succeed if mock-idp.js return different domain and protocol in assertion pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with RTCError('idp-execution-failure') if mock-idp.js throws error assert_equals: Expect initial pc.idpErrorInfo to be null expected (object) null but got (undefined) undefined
-FAIL getIdentityAssertion() should reject with RTCError('idp-bad-script-failure') if IdP proxy script do not register its callback pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with OperationError if mock-idp.js return invalid result pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with RTCError('idp-load-failure') if IdP cannot be loaded pc.setIdentityProvider is not a function
-FAIL getIdentityAssertion() should reject with RTCError('idp-need-login') when mock-idp.js requires login assert_equals: Expect initial pc.idpLoginUrl to be null expected (object) null but got (undefined) undefined
-FAIL setIdentityProvider() with no peerIdentity provided should use peerIdentity value from getConfiguration() pc.setIdentityProvider is not a function
-FAIL Calling setIdentityProvider() multiple times should reset identity assertions pc.setIdentityProvider is not a function
-FAIL createOffer() should return SDP containing identity assertion string if identity provider is set pc.setIdentityProvider is not a function
-FAIL createOffer() should reject with NotReadableError if identitity assertion request fails pc.setIdentityProvider is not a function
-FAIL createAnswer() should reject with NotReadableError if identitity assertion request fails pc.setIdentityProvider is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
index 43827774..14a75b0 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
@@ -3,14 +3,14 @@
 PASS getStats(null) should succeed
 PASS getStats() with track not added to connection should reject with InvalidAccessError
 PASS getStats() with track added via addTrack should succeed
-FAIL getStats() with track added via addTransceiver should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL getStats() with track associated with more than one sender should reject with InvalidAccessError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL getStats() with track associated with both sender and receiver should reject with InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+PASS getStats() with track added via addTransceiver should succeed
+PASS getStats() with track associated with more than one sender should reject with InvalidAccessError
+PASS getStats() with track associated with both sender and receiver should reject with InvalidAccessError
 PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
 FAIL getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
 FAIL getStats() with no argument should return stats for no-stream tracks assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
 FAIL getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
-FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats assert_true: Expect statsReport to contain stats object of type inbound-rtp expected true got false
 FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_true: Expect dictionary.dataChannelIdentifier to be integer expected true got false Reached unreachable code
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState-expected.txt
index b2860142..e3cf316 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState-expected.txt
@@ -1,7 +1,5 @@
 This is a testharness.js-based test.
 PASS Initial iceConnectionState should be new
-PASS Closing the connection should set iceConnectionState to closed
-PASS connection with one data channel should eventually have connected or completed connection state
 FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.html
index b647b3d3..4071033 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.html
@@ -64,12 +64,6 @@
     assert_equals(pc.iceConnectionState, 'new');
   }, 'Initial iceConnectionState should be new');
 
-  test(t => {
-    const pc = new RTCPeerConnection();
-    pc.close();
-    assert_equals(pc.iceConnectionState, 'closed');
-  }, 'Closing the connection should set iceConnectionState to closed');
-
   /*
     4.4.4 RTCIceConnectionState Enum
       checking
@@ -112,35 +106,6 @@
     const pc1 = new RTCPeerConnection();
     t.add_cleanup(() => pc1.close());
     const pc2 = new RTCPeerConnection();
-    t.add_cleanup(() => pc2.close());
-
-    let had_checking = false;
-
-    const onIceConnectionStateChange = t.step_func(() => {
-      const {iceConnectionState} = pc1;
-      if (iceConnectionState === 'checking') {
-        had_checking = true;
-      } else if (iceConnectionState === 'connected' ||
-                 iceConnectionState === 'completed') {
-        assert_true(had_checking, 'state should pass checking before' +
-                                  ' reaching connected or completed');
-        t.done();
-      }
-    });
-
-    pc1.createDataChannel('test');
-
-    pc1.addEventListener('iceconnectionstatechange', onIceConnectionStateChange);
-
-    exchangeIceCandidates(pc1, pc2);
-    doSignalingHandshake(pc1, pc2);
-  }, 'connection with one data channel should eventually have connected or ' +
-     'completed connection state');
-
-async_test(t => {
-    const pc1 = new RTCPeerConnection();
-    t.add_cleanup(() => pc1.close());
-    const pc2 = new RTCPeerConnection();
 
     t.add_cleanup(() => pc2.close());
 
@@ -151,22 +116,19 @@
         const iceTransport = pc1.sctp.transport.transport;
 
         assert_equals(iceTransport.state, 'checking',
-          'Expect ICE transport to be in checking state when' +
-          ' iceConnectionState is checking');
+          'Expect ICE transport to be in checking state when iceConnectionState is checking');
 
       } else if(iceConnectionState === 'connected') {
         const iceTransport = pc1.sctp.transport.transport;
 
         assert_equals(iceTransport.state, 'connected',
-          'Expect ICE transport to be in connected state when' +
-          ' iceConnectionState is connected');
+          'Expect ICE transport to be in connected state when iceConnectionState is connected');
 
       } else if(iceConnectionState === 'completed') {
         const iceTransport = pc1.sctp.transport.transport;
 
         assert_equals(iceTransport.state, 'completed',
-          'Expect ICE transport to be in connected state when' +
-          ' iceConnectionState is completed');
+          'Expect ICE transport to be in connected state when iceConnectionState is completed');
       }
     });
 
@@ -179,8 +141,7 @@
 
     exchangeIceCandidates(pc1, pc2);
     doSignalingHandshake(pc1, pc2);
-  }, 'connection with one data channel should eventually ' +
-     'have connected connection state');
+  }, 'connection with one data channel should eventually have connected connection state');
 
   /*
     TODO
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
index 64d94456..0861101 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
@@ -1,9 +1,9 @@
 This is a testharness.js-based test.
 PASS Creating first data channel should fire negotiationneeded event
 PASS calling createDataChannel twice should fire negotiationneeded event once
-FAIL addTransceiver() should fire negotiationneeded event Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL Calling addTransceiver() twice should fire negotiationneeded event once Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL Calling both addTransceiver() and createDataChannel() should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is rejected with: InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument. Reached unreachable code
+PASS addTransceiver() should fire negotiationneeded event
+FAIL Calling addTransceiver() twice should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is fulfilled with: [object Object] Reached unreachable code
+FAIL Calling both addTransceiver() and createDataChannel() should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is fulfilled with: [object Object] Reached unreachable code
 PASS negotiationneeded event should not fire if signaling state is not stable
 FAIL negotiationneeded event should fire only after signaling state go back to stable assert_unreached: Expect negotiationneeded promise to resolve after pc has set remote answer and go back to stable state Reached unreachable code
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-peerIdentity-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-peerIdentity-expected.txt
deleted file mode 100644
index 76951f1..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-peerIdentity-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL setRemoteDescription() on offer with a=identity should establish peerIdentity pc1.setIdentityProvider is not a function
-FAIL setRemoteDescription() on offer with a=identity that resolve to value different from target peer identity should reject with InvalidModificationError pc1.setIdentityProvider is not a function
-FAIL setRemoteDescription() with peerIdentity set and with IdP proxy that return validationAssertion with mismatch contents should reject with OperationError pc1.setIdentityProvider is not a function
-FAIL setRemoteDescription() and peerIdentity should reject with OperationError if IdP return validated identity that is different from its own domain pc1.setIdentityProvider is not a function
-FAIL When IdP throws error and pc has target peer identity, setRemoteDescription() and peerIdentity rejected with RTCError('idp-execution-error') pc1.setIdentityProvider is not a function
-FAIL IdP failure with no target peer identity should have following setRemoteDescription() succeed and replace pc.peerIdentity with a new promise pc1.setIdentityProvider is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
index bcf432cf..f3a08e1 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
@@ -1,9 +1,9 @@
 This is a testharness.js-based test.
-FAIL setLocalDescription(offer) with m= section should assign mid to corresponding transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setRemoteDescription(offer) with m= section and no existing transceiver should create corresponding transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setLocalDescription(rollback) should unset transceiver.mid Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setLocalDescription(rollback) should only unset transceiver mids associated with current round Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setRemoteDescription(rollback) should remove newly created transceiver from transceiver list Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setRemoteDescription should stop the transceiver if its corresponding m section is rejected promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS setLocalDescription(offer) with m= section should assign mid to corresponding transceiver
+PASS setRemoteDescription(offer) with m= section and no existing transceiver should create corresponding transceiver
+FAIL setLocalDescription(rollback) should unset transceiver.mid promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
+FAIL setLocalDescription(rollback) should only unset transceiver mids associated with current round promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
+FAIL setRemoteDescription(rollback) should remove newly created transceiver from transceiver list promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
+FAIL setRemoteDescription should stop the transceiver if its corresponding m section is rejected promise_test: Unhandled rejection with value: object "TypeError: pc2.getTransceivers(...)[0].stop is not a function"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
index 546ce00..57b1f47 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL setParameters with degradationPreference set should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setParameters with degradationPreference unset should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setParameters with degradationPreference set should succeed assert_equals: Expect initial param.degradationPreference to be balanced expected (string) "balanced" but got (undefined) undefined
+FAIL setParameters with degradationPreference unset should succeed assert_equals: Expect initial param.degradationPreference to be balanced expected (string) "balanced" but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
index 155de86..3ce1fdd6 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
@@ -1,27 +1,27 @@
 This is a testharness.js-based test.
-FAIL addTransceiver() with undefined sendEncodings should have default encoding parameter with active set to true promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL addTransceiver() with empty list sendEncodings should have default encoding parameter with active set to true promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL sender.getParameters() should return sendEncodings set by addTransceiver() promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL sender.setParameters() with mismatch number of encodings should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL sender.setParameters() with encodings unset should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.rid field should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with encoding.scaleResolutionDownBy field set to less than 1.0 should reject with RangeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with encoding.scaleResolutionDownBy field set to greater than 1.0 should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.dtx should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.dtx should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with unset encoding.dtx should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with unset encoding.dtx should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.active should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.active should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.priority should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.priority should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.networkPriority should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.networkPriority should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.ptime should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.ptime should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.maxBitrate should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.maxBitrate should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.maxFramerate should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL setParameters() with modified encoding.maxFramerate should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS addTransceiver() with undefined sendEncodings should have default encoding parameter with active set to true
+PASS addTransceiver() with empty list sendEncodings should have default encoding parameter with active set to true
+FAIL sender.getParameters() should return sendEncodings set by addTransceiver() promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Failed to set remote audio description send parameters."
+PASS sender.setParameters() with mismatch number of encodings should reject with InvalidModificationError
+PASS sender.setParameters() with encodings unset should reject with TypeError
+FAIL setParameters() with modified encoding.rid field should reject with InvalidModificationError assert_equals: expected (string) "foo" but got (undefined) undefined
+FAIL setParameters() with encoding.scaleResolutionDownBy field set to less than 1.0 should reject with RangeError assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL setParameters() with encoding.scaleResolutionDownBy field set to greater than 1.0 should succeed assert_approx_equals: expected a number but got a "undefined"
+FAIL setParameters() with modified encoding.dtx should succeed with RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
+FAIL setParameters() with modified encoding.dtx should succeed without RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
+FAIL setParameters() with unset encoding.dtx should succeed with RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
+FAIL setParameters() with unset encoding.dtx should succeed without RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
+PASS setParameters() with modified encoding.active should succeed with RTCRtpTransceiverInit
+PASS setParameters() with modified encoding.active should succeed without RTCRtpTransceiverInit
+PASS setParameters() with modified encoding.priority should succeed with RTCRtpTransceiverInit
+PASS setParameters() with modified encoding.priority should succeed without RTCRtpTransceiverInit
+PASS setParameters() with modified encoding.networkPriority should succeed with RTCRtpTransceiverInit
+PASS setParameters() with modified encoding.networkPriority should succeed without RTCRtpTransceiverInit
+FAIL setParameters() with modified encoding.ptime should succeed with RTCRtpTransceiverInit assert_equals: expected (number) 2 but got (undefined) undefined
+FAIL setParameters() with modified encoding.ptime should succeed without RTCRtpTransceiverInit assert_equals: expected (number) 2 but got (undefined) undefined
+PASS setParameters() with modified encoding.maxBitrate should succeed with RTCRtpTransceiverInit
+PASS setParameters() with modified encoding.maxBitrate should succeed without RTCRtpTransceiverInit
+FAIL setParameters() with modified encoding.maxFramerate should succeed with RTCRtpTransceiverInit assert_equals: expected (number) 24 but got (undefined) undefined
+FAIL setParameters() with modified encoding.maxFramerate should succeed without RTCRtpTransceiverInit assert_equals: expected (number) 24 but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
index cfb401b..4f8f48aa 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
-FAIL getParameters() with audio receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL getParameters() with video receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL getParameters() with simulcast video receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS getParameters() with audio receiver
+PASS getParameters() with video receiver
+FAIL getParameters() with simulcast video receiver promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': Attempted to create an encoder with more than 1 encoding parameter."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
index 56851d2..002ae802 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
 FAIL receiver.getStats() via addTrack should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
index 5351a21..f7302af8 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
 FAIL sender.getStats() via addTrack should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
index d16692f..8b4a12b3 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
@@ -1,12 +1,12 @@
 This is a testharness.js-based test.
-FAIL Calling replaceTrack on closed connection should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack with track of different kind should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack on stopped sender should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack on sender with null track and not set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack on sender not set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack(null) on sender not set to session description should resolve with sender.track set to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack(null) on sender set to session description should resolve with sender.track set to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack on sender with stopped track and and set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Calling replaceTrack on sender with similar track and and set to session description should resolve with sender.track set to new track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS Calling replaceTrack on closed connection should reject with InvalidStateError
+FAIL Calling replaceTrack with track of different kind should reject with TypeError assert_throws: function "function() { throw e }" threw object "InvalidModificationError" ("InvalidModificationError") expected object "TypeError" ("TypeError")
+FAIL Calling replaceTrack on stopped sender should reject with InvalidStateError promise_test: Unhandled rejection with value: object "TypeError: transceiver.stop is not a function"
+PASS Calling replaceTrack on sender with null track and not set to session description should resolve with sender.track set to given track
+PASS Calling replaceTrack on sender not set to session description should resolve with sender.track set to given track
+PASS Calling replaceTrack(null) on sender not set to session description should resolve with sender.track set to null
+PASS Calling replaceTrack(null) on sender set to session description should resolve with sender.track set to null
+PASS Calling replaceTrack on sender with stopped track and and set to session description should resolve with sender.track set to given track
+PASS Calling replaceTrack on sender with similar track and and set to session description should resolve with sender.track set to new track
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
index d75f3e39..2fc9877 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL setParameters() when transceiver is stopped should reject with InvalidStateError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setParameters() when transceiver is stopped should reject with InvalidStateError transceiver.stop is not a function
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
index 1b5df3af..a13c414 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
@@ -1,12 +1,12 @@
 This is a testharness.js-based test.
-FAIL setCodecPreferences() on audio transceiver with codecs returned from RTCRtpSender.getCapabilities('audio') should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() with both sender receiver codecs combined should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences([]) should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() with reordered codecs should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() with user defined codec should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() on audio transceiver with codecs returned from RTCRtpSender.getCapabilities('audio') should succeed transceiver.setCodecPreferences is not a function
+FAIL setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed transceiver.setCodecPreferences is not a function
+FAIL setCodecPreferences() with both sender receiver codecs combined should succeed transceiver.setCodecPreferences is not a function
+FAIL setCodecPreferences([]) should succeed transceiver.setCodecPreferences is not a function
+FAIL setCodecPreferences() with reordered codecs should succeed transceiver.setCodecPreferences is not a function
+FAIL setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(capabilities.codecs)" passed to assert_throws()
+FAIL setCodecPreferences() with user defined codec should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(codecs)" passed to assert_throws()
+FAIL setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(codecs)" passed to assert_throws()
+FAIL setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(codecs)" passed to assert_throws()
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setDirection-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setDirection-expected.txt
deleted file mode 100644
index 15f42daf..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-setDirection-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL setDirection should change transceiver.direction Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setDirection with same direction should have no effect Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL setDirection should change transceiver.direction independent of transceiver.currentDirection Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
index ce8e229..7b3d87c 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
@@ -1,10 +1,10 @@
 This is a testharness.js-based test.
-FAIL new RTCTrackEvent() with valid receiver, track, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL new RTCTrackEvent() with valid receiver, track, streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL new RTCTrackEvent() with valid receiver, track, multiple streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL new RTCTrackEvent() with unrelated receiver, track, streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL new RTCTrackEvent() with no transceiver should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL new RTCTrackEvent() with no track should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL new RTCTrackEvent() with no receiver should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with valid receiver, track, transceiver should succeed assert_equals: expected [] but got []
+PASS new RTCTrackEvent() with valid receiver, track, streams, transceiver should succeed
+PASS new RTCTrackEvent() with valid receiver, track, multiple streams, transceiver should succeed
+PASS new RTCTrackEvent() with unrelated receiver, track, streams, transceiver should succeed
+PASS new RTCTrackEvent() with no transceiver should throw TypeError
+PASS new RTCTrackEvent() with no track should throw TypeError
+PASS new RTCTrackEvent() with no receiver should throw TypeError
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index 493ac14..4e66c94 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 494 tests; 302 PASS, 192 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 494 tests; 339 PASS, 155 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
 FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
@@ -238,22 +238,22 @@
 FAIL RTCRtpSender interface: operation setStreams(MediaStream) assert_own_property: interface prototype object missing non-static operation expected property "setStreams" missing
 PASS RTCRtpSender interface: operation getStats()
 PASS RTCRtpSender interface: attribute dtmf
-FAIL RTCRtpSender must be primary interface of new RTCPeerConnection().addTransceiver('audio').sender assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Stringification of new RTCPeerConnection().addTransceiver('audio').sender assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "transport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "rtcpTransport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getCapabilities(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setParameters(RTCRtpSendParameters)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: calling setParameters(RTCRtpSendParameters) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getParameters()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "replaceTrack(MediaStreamTrack)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: calling replaceTrack(MediaStreamTrack) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setStreams(MediaStream)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: calling setStreams(MediaStream) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getStats()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "dtmf" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS RTCRtpSender must be primary interface of new RTCPeerConnection().addTransceiver('audio').sender
+PASS Stringification of new RTCPeerConnection().addTransceiver('audio').sender
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "track" with the proper type
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "transport" with the proper type
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "rtcpTransport" with the proper type assert_inherits: property "rtcpTransport" not found in prototype chain
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getCapabilities(DOMString)" with the proper type
+PASS RTCRtpSender interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setParameters(RTCRtpSendParameters)" with the proper type
+PASS RTCRtpSender interface: calling setParameters(RTCRtpSendParameters) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getParameters()" with the proper type
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "replaceTrack(MediaStreamTrack)" with the proper type
+PASS RTCRtpSender interface: calling replaceTrack(MediaStreamTrack) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setStreams(MediaStream)" with the proper type assert_inherits: property "setStreams" not found in prototype chain
+FAIL RTCRtpSender interface: calling setStreams(MediaStream) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_inherits: property "setStreams" not found in prototype chain
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getStats()" with the proper type
+PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "dtmf" with the proper type
 PASS RTCRtpReceiver interface: existence and properties of interface object
 PASS RTCRtpReceiver interface object length
 PASS RTCRtpReceiver interface object name
@@ -268,17 +268,17 @@
 PASS RTCRtpReceiver interface: operation getContributingSources()
 PASS RTCRtpReceiver interface: operation getSynchronizationSources()
 PASS RTCRtpReceiver interface: operation getStats()
-FAIL RTCRtpReceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio').receiver assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Stringification of new RTCPeerConnection().addTransceiver('audio').receiver assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "transport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "rtcpTransport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getCapabilities(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').receiver with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getParameters()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getContributingSources()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getSynchronizationSources()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getStats()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS RTCRtpReceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio').receiver
+PASS Stringification of new RTCPeerConnection().addTransceiver('audio').receiver
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "track" with the proper type
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "transport" with the proper type
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "rtcpTransport" with the proper type assert_inherits: property "rtcpTransport" not found in prototype chain
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getCapabilities(DOMString)" with the proper type
+PASS RTCRtpReceiver interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').receiver with too few arguments must throw TypeError
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getParameters()" with the proper type
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getContributingSources()" with the proper type
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getSynchronizationSources()" with the proper type
+PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getStats()" with the proper type
 PASS RTCRtpTransceiver interface: existence and properties of interface object
 PASS RTCRtpTransceiver interface object length
 PASS RTCRtpTransceiver interface object name
@@ -293,17 +293,17 @@
 PASS RTCRtpTransceiver interface: attribute currentDirection
 FAIL RTCRtpTransceiver interface: operation stop() assert_own_property: interface prototype object missing non-static operation expected property "stop" missing
 FAIL RTCRtpTransceiver interface: operation setCodecPreferences([object Object]) assert_own_property: interface prototype object missing non-static operation expected property "setCodecPreferences" missing
-FAIL RTCRtpTransceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio') assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Stringification of new RTCPeerConnection().addTransceiver('audio') assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "mid" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "sender" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "receiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stopped" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "direction" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "currentDirection" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stop()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "setCodecPreferences([object Object])" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCRtpTransceiver interface: calling setCodecPreferences([object Object]) on new RTCPeerConnection().addTransceiver('audio') with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS RTCRtpTransceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio')
+PASS Stringification of new RTCPeerConnection().addTransceiver('audio')
+PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "mid" with the proper type
+PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "sender" with the proper type
+PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "receiver" with the proper type
+PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stopped" with the proper type
+PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "direction" with the proper type
+PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "currentDirection" with the proper type
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stop()" with the proper type assert_inherits: property "stop" not found in prototype chain
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "setCodecPreferences([object Object])" with the proper type assert_inherits: property "setCodecPreferences" not found in prototype chain
+FAIL RTCRtpTransceiver interface: calling setCodecPreferences([object Object]) on new RTCPeerConnection().addTransceiver('audio') with too few arguments must throw TypeError assert_inherits: property "setCodecPreferences" not found in prototype chain
 PASS RTCDtlsTransport interface: existence and properties of interface object
 PASS RTCDtlsTransport interface object length
 PASS RTCDtlsTransport interface object name
@@ -366,12 +366,12 @@
 PASS RTCTrackEvent interface: attribute track
 PASS RTCTrackEvent interface: attribute streams
 PASS RTCTrackEvent interface: attribute transceiver
-FAIL RTCTrackEvent must be primary interface of initTrackEvent() assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL Stringification of initTrackEvent() assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "receiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
-FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS RTCTrackEvent must be primary interface of initTrackEvent()
+PASS Stringification of initTrackEvent()
+PASS RTCTrackEvent interface: initTrackEvent() must inherit property "receiver" with the proper type
+PASS RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type
+PASS RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type
+PASS RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type
 FAIL RTCSctpTransport interface: existence and properties of interface object assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
 FAIL RTCSctpTransport interface object length assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
 FAIL RTCSctpTransport interface object name assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/interfaces.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/interfaces.https-expected.txt
deleted file mode 100644
index 090c43b..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/interfaces.https-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS Main test driver
-PASS Test driver for asyncInitCertificate
-FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
-PASS Test driver for asyncInitMediaStreamTrack
-FAIL EventTarget interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
-PASS EventTarget interface object length
-PASS EventTarget interface object name
-PASS EventTarget interface: existence and properties of interface prototype object
-PASS EventTarget interface: existence and properties of interface prototype object's "constructor" property
-PASS EventTarget interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaStreamTrack interface: existence and properties of interface object
-PASS MediaStreamTrack interface object length
-PASS MediaStreamTrack interface object name
-PASS MediaStreamTrack interface: existence and properties of interface prototype object
-PASS MediaStreamTrack interface: existence and properties of interface prototype object's "constructor" property
-PASS MediaStreamTrack interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaStreamTrack must be primary interface of idlTestObjects.mediaStreamTrack
-PASS Stringification of idlTestObjects.mediaStreamTrack
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
index 09d40c0a..9714f8a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
@@ -1,21 +1,21 @@
 This is a testharness.js-based test.
-FAIL createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers assert_equals: Expect audio line to remain in created offer expected 1 but got 0
-FAIL createOffer() with offerToReceiveVideo should add video line to all subsequent created offers assert_equals: Expect video line to remain in created offer expected 1 but got 0
-FAIL createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line assert_equals: Expect audio line to remain in created offer expected 1 but got 0
+PASS createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers
+PASS createOffer() with offerToReceiveVideo should add video line to all subsequent created offers
+PASS createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line
 PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
 FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
 FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists
+FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
+FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
 PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
 FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
 FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
-FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists
+FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
+FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
 FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
index 1c91b50..0485f08a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL checkAddTransceiverWithStream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS checkAddTransceiverWithStream
 FAIL checkAddTransceiverWithOfferToReceiveAudio assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
 FAIL checkAddTransceiverWithOfferToReceiveVideo assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
 FAIL checkAddTransceiverWithOfferToReceiveBoth assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false},{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/protocol/video-codecs.https-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/protocol/video-codecs.https-expected.txt
new file mode 100644
index 0000000..6510131a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/protocol/video-codecs.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL H.264 and VP8 should be supported in initial offer assert_true: H.264 is supported expected true got false
+FAIL H.264 and VP8 should be negotiated after handshake assert_true: expected true got false
+PASS All H.264 codecs MUST include profile-level-id
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webxr/OWNERS b/third_party/blink/web_tests/external/wpt/webxr/OWNERS
index cb7f472..76fdcdd 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/webxr/OWNERS
@@ -2,4 +2,4 @@
 
 # TEAM: xr-dev@chromium.org
 # COMPONENT: Blink>WebXR
-# WPT-NOTIFY: true
+# WPT-NOTIFY: false # TODO(crbug.com/923060): Enable when spec stabilizes.
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-datachannel.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-datachannel.html
index 73590e09..bbfe876 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-datachannel.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-datachannel.html
@@ -90,13 +90,9 @@
     shouldNotThrow("dc.send('xyzzy');");
 }
 
-var pc_has_connected = false;
 function pc_onicechange() {
-    if ((pc.iceConnectionState === "connected" ||
-        pc.iceConnectionState === "completed") &&
-        !pc_has_connected) {
+    if (pc.iceConnectionState === "completed") {
         testPassed("pc is connected");
-        pc_has_connected = true;
     }
 }
 
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getRemoteStreams.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getRemoteStreams.html
index f28f13b6..b69eaabe 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getRemoteStreams.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getRemoteStreams.html
@@ -10,9 +10,11 @@
 // This is not an external/wpt/webrtc/ test because it tests APIs and behaviors
 // that are not in the spec.
 
-promise_test(function() {
-  let caller = new RTCPeerConnection();
-  let callee = new RTCPeerConnection();
+promise_test(async t => {
+  const caller = new RTCPeerConnection();
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection();
+  t.add_cleanup(() => callee.close());
   return navigator.mediaDevices.getUserMedia({audio:true})
   .then((stream) => {
     caller.addStream(stream);
@@ -25,9 +27,11 @@
 }, 'callee.getRemoteStreams() should match caller.getLocalStreams() for ' +
    'one stream with one track.');
 
-promise_test(function() {
-  let caller = new RTCPeerConnection();
-  let callee = new RTCPeerConnection();
+promise_test(async t => {
+  const caller = new RTCPeerConnection();
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection();
+  t.add_cleanup(() => callee.close());
   return navigator.mediaDevices.getUserMedia({audio:true, video:true})
   .then((stream) => {
     caller.addStream(stream);
@@ -40,9 +44,11 @@
 }, 'callee.getRemoteStreams() should match caller.getLocalStreams() for ' +
    'one stream with two tracks.');
 
-promise_test(function() {
-  let caller = new RTCPeerConnection();
-  let callee = new RTCPeerConnection();
+promise_test(async t => {
+  const caller = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => callee.close());
   let localStreams = null;
   return Promise.all([
       navigator.mediaDevices.getUserMedia({audio:true, video:true}),
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getSenders.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getSenders.html
index f946ca5..bcffc37 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getSenders.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-getSenders.html
@@ -7,8 +7,9 @@
 </head>
 <body>
 <script>
-promise_test(function() {
-  let pc = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   return createStreams({audio:true}, 1)
     .then(function(streams) {
       for (let i = 0; i < streams.length; ++i) {
@@ -21,8 +22,9 @@
     });
 }, 'getSenders() for a stream with a single audio track.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   return createStreams({video:true}, 1)
     .then(function(streams) {
       for (let i = 0; i < streams.length; ++i) {
@@ -35,8 +37,9 @@
     });
 }, 'getSenders() for a stream with a single video track.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   return createStreams({audio:true, video:true}, 1)
     .then(function(streams) {
       for (let i = 0; i < streams.length; ++i) {
@@ -49,8 +52,9 @@
     });
 }, 'getSenders() for a stream with an audio and video track.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   return createStreams({audio:true, video:true}, 2)
     .then(function(streams) {
       for (let i = 0; i < streams.length; ++i) {
@@ -63,8 +67,9 @@
     });
 }, 'getSenders() for a multiple audio-video streams.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   let senders = new Set();
   return createStreams({audio:true, video:true}, 2)
     .then(function(streams) {
@@ -117,8 +122,9 @@
     });
 }, 'getSenders() for streams being added and removed.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
       let track = streams[0].getAudioTracks()[0];
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-APIs.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-APIs.html
index 7a04ffd2..d38df2e7 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-APIs.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-legacy-stream-APIs.html
@@ -10,7 +10,8 @@
 // that are not in the spec.
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   pc.addStream(stream);
   assert_array_equals(pc.getLocalStreams(), [ stream ]);
@@ -19,7 +20,8 @@
 }, 'addStream() adds to local streams and senders.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   let sender = pc.addTrack(stream.getTracks()[0], stream);
   assert_array_equals(pc.getLocalStreams(), [ stream ]);
@@ -27,7 +29,8 @@
 }, 'addTrack() adds to local streams and senders.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   pc.addStream(stream);
   try {
@@ -40,7 +43,8 @@
 }, 'addTrack() fails after addStream().');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   let sender = pc.addTrack(stream.getTracks()[0]);
@@ -56,7 +60,8 @@
 }, 'addStream() after addTrack() adds the remaining track.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   let videoTrack = stream.getVideoTracks()[0];
@@ -73,7 +78,8 @@
 }, 'Adding a track to an addStream()-stream adds it to the PC.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   let videoTrack = stream.getVideoTracks()[0];
@@ -88,7 +94,8 @@
 }, 'Removing a track from an addStream()-stream removes it from the PC.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   let videoTrack = stream.getVideoTracks()[0];
@@ -100,7 +107,8 @@
 }, 'Adding a track to an addStream()-stream after the PC closed is safe.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   let videoTrack = stream.getVideoTracks()[0];
@@ -111,7 +119,8 @@
 }, 'Removing a track from an addStream()-stream after the PC closed is safe.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   let videoTrack = stream.getVideoTracks()[0];
@@ -129,7 +138,8 @@
 }, 'The PC stops observing the stream after removeStream().');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   pc.addStream(stream);
   assert_array_equals(pc.getLocalStreams(), [ stream ]);
@@ -140,7 +150,8 @@
 }, 'removeStream() after addStream() removes from local streams and senders.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   pc.addTrack(stream.getTracks()[0], stream);
   assert_array_equals(pc.getLocalStreams(), [ stream ]);
@@ -151,7 +162,8 @@
 }, 'removeStream() after addTrack() removes from local streams and senders.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true,
                                                           video:true});
   pc.addStream(stream);
@@ -167,7 +179,8 @@
 }, 'removeStream() after removeTrack() removes remaining tracks.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   pc.addStream(stream);
   assert_array_equals(pc.getLocalStreams(), [ stream ]);
@@ -178,7 +191,8 @@
 }, 'removeTrack() after addStream() removes from local streams and senders.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   let sender = pc.addTrack(stream.getTracks()[0], stream);
   assert_array_equals(pc.getLocalStreams(), [ stream ]);
@@ -189,14 +203,16 @@
 }, 'removeTrack() after addTrack() removes from local streams and senders.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   pc.addStream(stream);
   pc.createDTMFSender(stream.getTracks()[0]);
 }, 'createDTMFSender() with addStream()-track.');
 
 promise_test(async t => {
-  let pc = new RTCPeerConnection();
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
   let stream = await navigator.mediaDevices.getUserMedia({audio:true});
   let track = stream.getTracks()[0];
   pc.addTrack(track);
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-ontrack.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-ontrack.html
index e593dc6..1037e213 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-ontrack.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-ontrack.html
@@ -10,9 +10,11 @@
 
 // TODO(hbos): Consider removing this file, it duplicates wpt tests.
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   let eventSequence = [];
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
@@ -33,9 +35,11 @@
     });
 }, 'ontrack fires before negotiation done.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   let eventSequence = [];
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
@@ -56,9 +60,11 @@
     });
 }, 'onaddstream fires before negotation done.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection({sdpSemantics:'plan-b'});
+  t.add_cleanup(() => pc2.close());
   let eventSequence = [];
   return createStreams({audio:true, video:false}, 2)
     .then(function(streams) {
@@ -86,9 +92,11 @@
     });
 }, 'ontrack and onaddstream fires in the expected relative order.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   let eventSequence = [];
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
@@ -116,9 +124,11 @@
     });
 }, 'ontrack and onaddstream fires in the same task.');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
       pc.addStream(streams[0]);
@@ -134,9 +144,11 @@
     });
 }, 'ontrack event\'s track matches track in getRemoteStreams().');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
       pc.addStream(streams[0]);
@@ -151,9 +163,11 @@
     });
 }, 'ontrack event\'s receiver matches getReceivers().');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   return createStreams({audio:true, video:false}, 1)
     .then(function(streams) {
       pc.addStream(streams[0]);
@@ -167,9 +181,11 @@
     });
 }, 'ontrack event\'s streams match getRemoteStreams().');
 
-promise_test(function() {
-  let pc = new RTCPeerConnection();
-  let pc2 = new RTCPeerConnection();
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  t.add_cleanup(() => pc.close());
+  const pc2 = new RTCPeerConnection();
+  t.add_cleanup(() => pc2.close());
   let firedForAudioTrack = false;
   let firedForVideoTrack = false;
   return createStreams({audio:true, video:true}, 1)
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/background/full-viewport-repaint-for-background-attachment-fixed-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/background/full-viewport-repaint-for-background-attachment-fixed-expected.txt
index 5d1244a..63fe5f66 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/background/full-viewport-repaint-for-background-attachment-fixed-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/paint/invalidation/background/full-viewport-repaint-for-background-attachment-fixed-expected.txt
@@ -10,11 +10,6 @@
           "object": "Scrolling background of LayoutView #document",
           "rect": [0, 0, 785, 5000],
           "reason": "background"
-        },
-        {
-          "object": "InlineTextBox 'Tests that scrolling a frame with background-attachment: fixed invalidates the entire viewport.'",
-          "rect": [0, 0, 589, 19],
-          "reason": "disappeared"
         }
       ],
       "transform": 1
diff --git a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver-enabled.html b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver-enabled.html
deleted file mode 100644
index 44246ebf..0000000
--- a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver-enabled.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<html>
-<title>Tests for 'Save-Data' header.</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<!-- Enable save data header and the JavaScript attribute -->
-<script> internals.setSaveDataEnabled(true); </script>
-
-<iframe srcdoc="
-<!DOCTYPE html>
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-
-<script>
-internals.setSaveDataEnabled(true);
-
-var CHECK_PATH = './resources/check-save-data-header.php';
-var METHODS = ['GET', 'POST', 'PUT'];
-var REQUESTS =
-    METHODS.map(method => new Request(CHECK_PATH, {method: method}));
-
-window.top.promise_test(t => {
-  internals.setSaveDataEnabled(true);
-  return Promise.all(REQUESTS.map(request => fetch(request.clone())))
-    .then(responses => Promise.all(responses.map(response => response.text())))
-    .then(texts => {
-        for (var i = 0; i < METHODS.length; ++i) {
-          assert_equals(
-              texts[i], 'Save-Data: on',
-              'Save-Data header should be sent when enabled. method: ' +
-              METHODS[i]);
-        }
-      });
-}, 'fetch() from document with Data-Saver enabled.');
-
-window.top.promise_test(t => {
-  internals.setSaveDataEnabled(true);
-  var worker =
-    new Worker('./resources/data-saver-worker.php?dedicated-enabled');
-  return new Promise(resolve =>
-                     worker.addEventListener('message', resolve))
-     .then(msg => {
-         var result = msg.data;
-         assert_equals(
-             result['worker_script_header'], 'Save-Data: on',
-             'Save-Data header should be sent for worker script when enabled.');
-         for (var i = 0; i < METHODS.length; ++i) {
-           assert_equals(
-               result[METHODS[i]], 'Save-Data: on',
-               'Save-Data header should be sent when enabled. method: ' +
-               METHODS[i]);
-         }
-       });
-}, 'fetch() from dedicated worker with Data-Saver enabled.');
-
-// A test for shared workers is implemented as a browser test because shared
-// worker script is requested from the browser process when NetworkService is
-// enabled, and internals.setSaveDataEnable() doesn't work in that case.
-
-</script>
-"></iframe>
-
-</html>
diff --git a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html
index 560b6f2..9777ce6a 100644
--- a/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html
+++ b/third_party/blink/web_tests/http/tests/fetch/chromium/data-saver.html
@@ -61,4 +61,62 @@
 </script>
 "></iframe>
 
+
+<!-- Enable save data header and the JavaScript attribute -->
+<script> internals.setSaveDataEnabled(true); </script>
+
+<iframe srcdoc="
+<!DOCTYPE html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<script>
+internals.setSaveDataEnabled(true);
+
+var CHECK_PATH = './resources/check-save-data-header.php';
+var METHODS = ['GET', 'POST', 'PUT'];
+var REQUESTS =
+    METHODS.map(method => new Request(CHECK_PATH, {method: method}));
+
+window.top.promise_test(t => {
+  internals.setSaveDataEnabled(true);
+  return Promise.all(REQUESTS.map(request => fetch(request.clone())))
+    .then(responses => Promise.all(responses.map(response => response.text())))
+    .then(texts => {
+        for (var i = 0; i < METHODS.length; ++i) {
+          assert_equals(
+              texts[i], 'Save-Data: on',
+              'Save-Data header should be sent when disabled. method: ' +
+              METHODS[i]);
+        }
+      });
+}, 'fetch() from document with Data-Saver enabled.');
+
+window.top.promise_test(t => {
+  internals.setSaveDataEnabled(true);
+  var worker =
+    new Worker('./resources/data-saver-worker.php?dedicated-enabled');
+  return new Promise(resolve =>
+                     worker.addEventListener('message', resolve))
+     .then(msg => {
+         var result = msg.data;
+         assert_equals(
+             result['worker_script_header'], 'Save-Data: on',
+             'Save-Data header should be sent for worker script when enabled.');
+         for (var i = 0; i < METHODS.length; ++i) {
+           assert_equals(
+               result[METHODS[i]], 'Save-Data: on',
+               'Save-Data header should be sent when enabled. method: ' +
+               METHODS[i]);
+         }
+       });
+}, 'fetch() from dedicated worker with Data-Saver enabled.');
+
+// A test for shared workers is implemented as a browser test because shared
+// worker script is requested from the browser process when NetworkService is
+// enabled, and internals.setSaveDataEnable() doesn't work in that case.
+
+</script>
+"></iframe>
+
 </html>
diff --git a/third_party/blink/web_tests/virtual/threaded/external/wpt/animation-worklet/README.txt b/third_party/blink/web_tests/virtual/threaded/external/wpt/animation-worklet/README.txt
new file mode 100644
index 0000000..7f4b48be
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/external/wpt/animation-worklet/README.txt
@@ -0,0 +1,2 @@
+# This suite runs external/wpt/animation-worklet tests with threaded
+# compositing enabled.
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/README.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/README.txt
new file mode 100644
index 0000000..9e16327
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/README.txt
@@ -0,0 +1,2 @@
+# This suite runs external/wpt/webrtc/ with
+# --disable-features=RTCUnifiedPlanByDefault.
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
new file mode 100644
index 0000000..f03a6e2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS insertDTMF() should succeed if tones contains valid DTMF characters
+PASS insertDTMF() should throw InvalidCharacterError if tones contains invalid DTMF characters
+FAIL insertDTMF() should throw InvalidStateError if transceiver is stopped Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL insertDTMF() should throw InvalidStateError if transceiver.currentDirection is recvonly promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL insertDTMF() should throw InvalidStateError if transceiver.currentDirection is inactive promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+PASS insertDTMF() should set toneBuffer to provided tones normalized, with old tones overridden
+PASS insertDTMF() after remove and close should reject
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
similarity index 70%
rename from third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
index 622e37be..f2bcb704 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
@@ -1,16 +1,15 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: transceiver.stop is not a function
 PASS insertDTMF() with default duration and intertoneGap should fire tonechange events at the expected time
 PASS insertDTMF() with explicit duration and intertoneGap should fire tonechange events at the expected time
 PASS insertDTMF('') should not fire any tonechange event, including for '' tone
 PASS insertDTMF() with duration less than 40 should be clamped to 40
 PASS insertDTMF() with interToneGap less than 30 should be clamped to 30
 PASS insertDTMF with comma should delay next tonechange event for a constant 2000ms
-FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_greater_than: More tonechange event is fired than expected expected a number greater than 0 but got 0
+FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_unreached: Unexpected promise rejection: Error: assert_equals: Expect there to be only one tranceiver in pc expected 1 but got 0 Reached unreachable code
 PASS Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones
 PASS Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones
 PASS Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing
-FAIL Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing Failed to execute 'insertDTMF' on 'RTCDTMFSender': The 'canInsertDTMF' attribute is false: this sender cannot send DTMF.
+FAIL Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
 PASS Tone change event constructor works
 PASS Tone change event with unexpected name should not crash
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
new file mode 100644
index 0000000..e83cef9
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL DTLS transport goes to connected state promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+FAIL close() causes the local transport to close immediately promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+FAIL close() causes the other end's DTLS transport to close promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
new file mode 100644
index 0000000..99faa3c4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
@@ -0,0 +1,25 @@
+This is a testharness.js-based test.
+FAIL addTransceiver() with string argument as invalid kind should throw TypeError assert_throws: function "() => pc.addTransceiver('invalid')" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument." ("InvalidStateError") expected object "TypeError" ("TypeError")
+FAIL addTransceiver('audio') should return an audio transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL addTransceiver('video') should return a video transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL addTransceiver() with direction sendonly should have result transceiver.direction be the same Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL addTransceiver() with direction inactive should have result transceiver.direction be the same Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+PASS addTransceiver() with invalid direction should throw TypeError
+FAIL addTransceiver(track) should have result with sender.track be given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL addTransceiver(track) multiple times should create multiple transceivers promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL addTransceiver() with rid containing invalid non-alphanumeric characters should throw TypeError assert_throws: function "() =>
+      pc.addTransceiver('audio', {
+        sendEncodings: [{
+          rid: '@Invalid!'
+        }]
+      })" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument." ("InvalidStateError") expected object "TypeError" ("TypeError")
+FAIL addTransceiver() with rid longer than 16 characters should throw TypeError assert_throws: function "() =>
+      pc.addTransceiver('audio', {
+        sendEncodings: [{
+          rid: 'a'.repeat(17)
+        }]
+      })" threw object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument." ("InvalidStateError") expected object "TypeError" ("TypeError")
+FAIL addTransceiver() with valid rid value should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL addTransceiver() with valid sendEncodings should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
similarity index 61%
rename from third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
index 3ee7bf7..3f7a47d 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createOffer-expected.txt
@@ -2,6 +2,6 @@
 FAIL createOffer() with no argument from newly created RTCPeerConnection should succeed assert_false: Expect offer to not be instance of RTCSessionDescription expected false got true
 PASS createOffer() and then setLocalDescription() should succeed
 PASS createOffer() after connection is closed should reject with InvalidStateError
-FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream assert_equals: Expect m=audio line to be found in offer SDP expected 1 but got 0
+FAIL When media stream is added when createOffer() is running in parallel, the result offer should contain the new media stream Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
new file mode 100644
index 0000000..43827774
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS getStats() with no argument should succeed
+PASS getStats(null) should succeed
+PASS getStats() with track not added to connection should reject with InvalidAccessError
+PASS getStats() with track added via addTrack should succeed
+FAIL getStats() with track added via addTransceiver should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL getStats() with track associated with more than one sender should reject with InvalidAccessError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL getStats() with track associated with both sender and receiver should reject with InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
+FAIL getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
+FAIL getStats() with no argument should return stats for no-stream tracks assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
+FAIL getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
+FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_true: Expect dictionary.dataChannelIdentifier to be integer expected true got false Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
new file mode 100644
index 0000000..64d94456
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS Creating first data channel should fire negotiationneeded event
+PASS calling createDataChannel twice should fire negotiationneeded event once
+FAIL addTransceiver() should fire negotiationneeded event Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL Calling addTransceiver() twice should fire negotiationneeded event once Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL Calling both addTransceiver() and createDataChannel() should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is rejected with: InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument. Reached unreachable code
+PASS negotiationneeded event should not fire if signaling state is not stable
+FAIL negotiationneeded event should fire only after signaling state go back to stable assert_unreached: Expect negotiationneeded promise to resolve after pc has set remote answer and go back to stable state Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
new file mode 100644
index 0000000..bcf432cf
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+FAIL setLocalDescription(offer) with m= section should assign mid to corresponding transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setRemoteDescription(offer) with m= section and no existing transceiver should create corresponding transceiver Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setLocalDescription(rollback) should unset transceiver.mid Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setLocalDescription(rollback) should only unset transceiver mids associated with current round Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setRemoteDescription(rollback) should remove newly created transceiver from transceiver list Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setRemoteDescription should stop the transceiver if its corresponding m section is rejected promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
new file mode 100644
index 0000000..546ce00
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL setParameters with degradationPreference set should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setParameters with degradationPreference unset should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
new file mode 100644
index 0000000..155de86
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
@@ -0,0 +1,27 @@
+This is a testharness.js-based test.
+FAIL addTransceiver() with undefined sendEncodings should have default encoding parameter with active set to true promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL addTransceiver() with empty list sendEncodings should have default encoding parameter with active set to true promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL sender.getParameters() should return sendEncodings set by addTransceiver() promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL sender.setParameters() with mismatch number of encodings should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL sender.setParameters() with encodings unset should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.rid field should reject with InvalidModificationError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with encoding.scaleResolutionDownBy field set to less than 1.0 should reject with RangeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with encoding.scaleResolutionDownBy field set to greater than 1.0 should succeed promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.dtx should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.dtx should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with unset encoding.dtx should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with unset encoding.dtx should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.active should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.active should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.priority should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.priority should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.networkPriority should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.networkPriority should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.ptime should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.ptime should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.maxBitrate should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.maxBitrate should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.maxFramerate should succeed with RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL setParameters() with modified encoding.maxFramerate should succeed without RTCRtpTransceiverInit promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
new file mode 100644
index 0000000..cfb401b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+FAIL getParameters() with audio receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL getParameters() with video receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL getParameters() with simulcast video receiver promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
new file mode 100644
index 0000000..56851d2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL receiver.getStats() via addTrack should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
new file mode 100644
index 0000000..5351a21
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL sender.getStats() via addTrack should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
new file mode 100644
index 0000000..d16692f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+FAIL Calling replaceTrack on closed connection should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack with track of different kind should reject with TypeError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack on stopped sender should reject with InvalidStateError promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack on sender with null track and not set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack on sender not set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack(null) on sender not set to session description should resolve with sender.track set to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack(null) on sender set to session description should resolve with sender.track set to null promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack on sender with stopped track and and set to session description should resolve with sender.track set to given track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Calling replaceTrack on sender with similar track and and set to session description should resolve with sender.track set to new track promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
new file mode 100644
index 0000000..d75f3e39
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL setParameters() when transceiver is stopped should reject with InvalidStateError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
new file mode 100644
index 0000000..1b5df3af
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+FAIL setCodecPreferences() on audio transceiver with codecs returned from RTCRtpSender.getCapabilities('audio') should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() with both sender receiver codecs combined should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences([]) should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() with reordered codecs should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() with user defined codec should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidAccessError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver.https-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
new file mode 100644
index 0000000..ce8e229
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+FAIL new RTCTrackEvent() with valid receiver, track, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with valid receiver, track, streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with valid receiver, track, multiple streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with unrelated receiver, track, streams, transceiver should succeed Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with no transceiver should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with no track should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+FAIL new RTCTrackEvent() with no receiver should throw TypeError Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
similarity index 72%
rename from third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
index 4e66c94..493ac14 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 494 tests; 339 PASS, 155 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 494 tests; 302 PASS, 192 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
 FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
@@ -238,22 +238,22 @@
 FAIL RTCRtpSender interface: operation setStreams(MediaStream) assert_own_property: interface prototype object missing non-static operation expected property "setStreams" missing
 PASS RTCRtpSender interface: operation getStats()
 PASS RTCRtpSender interface: attribute dtmf
-PASS RTCRtpSender must be primary interface of new RTCPeerConnection().addTransceiver('audio').sender
-PASS Stringification of new RTCPeerConnection().addTransceiver('audio').sender
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "track" with the proper type
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "transport" with the proper type
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "rtcpTransport" with the proper type assert_inherits: property "rtcpTransport" not found in prototype chain
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getCapabilities(DOMString)" with the proper type
-PASS RTCRtpSender interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setParameters(RTCRtpSendParameters)" with the proper type
-PASS RTCRtpSender interface: calling setParameters(RTCRtpSendParameters) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getParameters()" with the proper type
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "replaceTrack(MediaStreamTrack)" with the proper type
-PASS RTCRtpSender interface: calling replaceTrack(MediaStreamTrack) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError
-FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setStreams(MediaStream)" with the proper type assert_inherits: property "setStreams" not found in prototype chain
-FAIL RTCRtpSender interface: calling setStreams(MediaStream) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_inherits: property "setStreams" not found in prototype chain
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getStats()" with the proper type
-PASS RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "dtmf" with the proper type
+FAIL RTCRtpSender must be primary interface of new RTCPeerConnection().addTransceiver('audio').sender assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Stringification of new RTCPeerConnection().addTransceiver('audio').sender assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "transport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "rtcpTransport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getCapabilities(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setParameters(RTCRtpSendParameters)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: calling setParameters(RTCRtpSendParameters) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getParameters()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "replaceTrack(MediaStreamTrack)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: calling replaceTrack(MediaStreamTrack) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "setStreams(MediaStream)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: calling setStreams(MediaStream) on new RTCPeerConnection().addTransceiver('audio').sender with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "getStats()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpSender interface: new RTCPeerConnection().addTransceiver('audio').sender must inherit property "dtmf" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 PASS RTCRtpReceiver interface: existence and properties of interface object
 PASS RTCRtpReceiver interface object length
 PASS RTCRtpReceiver interface object name
@@ -268,17 +268,17 @@
 PASS RTCRtpReceiver interface: operation getContributingSources()
 PASS RTCRtpReceiver interface: operation getSynchronizationSources()
 PASS RTCRtpReceiver interface: operation getStats()
-PASS RTCRtpReceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio').receiver
-PASS Stringification of new RTCPeerConnection().addTransceiver('audio').receiver
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "track" with the proper type
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "transport" with the proper type
-FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "rtcpTransport" with the proper type assert_inherits: property "rtcpTransport" not found in prototype chain
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getCapabilities(DOMString)" with the proper type
-PASS RTCRtpReceiver interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').receiver with too few arguments must throw TypeError
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getParameters()" with the proper type
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getContributingSources()" with the proper type
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getSynchronizationSources()" with the proper type
-PASS RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getStats()" with the proper type
+FAIL RTCRtpReceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio').receiver assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Stringification of new RTCPeerConnection().addTransceiver('audio').receiver assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "transport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "rtcpTransport" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getCapabilities(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: calling getCapabilities(DOMString) on new RTCPeerConnection().addTransceiver('audio').receiver with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getParameters()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getContributingSources()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getSynchronizationSources()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpReceiver interface: new RTCPeerConnection().addTransceiver('audio').receiver must inherit property "getStats()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 PASS RTCRtpTransceiver interface: existence and properties of interface object
 PASS RTCRtpTransceiver interface object length
 PASS RTCRtpTransceiver interface object name
@@ -293,17 +293,17 @@
 PASS RTCRtpTransceiver interface: attribute currentDirection
 FAIL RTCRtpTransceiver interface: operation stop() assert_own_property: interface prototype object missing non-static operation expected property "stop" missing
 FAIL RTCRtpTransceiver interface: operation setCodecPreferences([object Object]) assert_own_property: interface prototype object missing non-static operation expected property "setCodecPreferences" missing
-PASS RTCRtpTransceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio')
-PASS Stringification of new RTCPeerConnection().addTransceiver('audio')
-PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "mid" with the proper type
-PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "sender" with the proper type
-PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "receiver" with the proper type
-PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stopped" with the proper type
-PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "direction" with the proper type
-PASS RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "currentDirection" with the proper type
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stop()" with the proper type assert_inherits: property "stop" not found in prototype chain
-FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "setCodecPreferences([object Object])" with the proper type assert_inherits: property "setCodecPreferences" not found in prototype chain
-FAIL RTCRtpTransceiver interface: calling setCodecPreferences([object Object]) on new RTCPeerConnection().addTransceiver('audio') with too few arguments must throw TypeError assert_inherits: property "setCodecPreferences" not found in prototype chain
+FAIL RTCRtpTransceiver must be primary interface of new RTCPeerConnection().addTransceiver('audio') assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Stringification of new RTCPeerConnection().addTransceiver('audio') assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "mid" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "sender" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "receiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stopped" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "direction" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "currentDirection" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "stop()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: new RTCPeerConnection().addTransceiver('audio') must inherit property "setCodecPreferences([object Object])" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCRtpTransceiver interface: calling setCodecPreferences([object Object]) on new RTCPeerConnection().addTransceiver('audio') with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 PASS RTCDtlsTransport interface: existence and properties of interface object
 PASS RTCDtlsTransport interface object length
 PASS RTCDtlsTransport interface object name
@@ -366,12 +366,12 @@
 PASS RTCTrackEvent interface: attribute track
 PASS RTCTrackEvent interface: attribute streams
 PASS RTCTrackEvent interface: attribute transceiver
-PASS RTCTrackEvent must be primary interface of initTrackEvent()
-PASS Stringification of initTrackEvent()
-PASS RTCTrackEvent interface: initTrackEvent() must inherit property "receiver" with the proper type
-PASS RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type
-PASS RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type
-PASS RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type
+FAIL RTCTrackEvent must be primary interface of initTrackEvent() assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL Stringification of initTrackEvent() assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "receiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "track" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "streams" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
+FAIL RTCTrackEvent interface: initTrackEvent() must inherit property "transceiver" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL RTCSctpTransport interface: existence and properties of interface object assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
 FAIL RTCSctpTransport interface object length assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
 FAIL RTCSctpTransport interface object name assert_own_property: self does not have own property "RTCSctpTransport" expected property "RTCSctpTransport" missing
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
index 569d63c0..09d40c0a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
@@ -1,4 +1,7 @@
 This is a testharness.js-based test.
+FAIL createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers assert_equals: Expect audio line to remain in created offer expected 1 but got 0
+FAIL createOffer() with offerToReceiveVideo should add video line to all subsequent created offers assert_equals: Expect video line to remain in created offer expected 1 but got 0
+FAIL createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line assert_equals: Expect audio line to remain in created offer expected 1 but got 0
 PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
 FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
 FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
similarity index 65%
rename from third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
index 0485f08a..1c91b50 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/RTCRtpTransceiver-with-OfferToReceive-options.https-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-PASS checkAddTransceiverWithStream
+FAIL checkAddTransceiverWithStream promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'. 'unified-plan' will become the default behavior in the future, but it is currently experimental. To try it out, construct the RTCPeerConnection with sdpSemantics:'unified-plan' present in the RTCConfiguration argument."
 FAIL checkAddTransceiverWithOfferToReceiveAudio assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false}]" but got "[]"
 FAIL checkAddTransceiverWithOfferToReceiveVideo assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
 FAIL checkAddTransceiverWithOfferToReceiveBoth assert_equals: expected "[{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"audio\"}},sender:{track:null},stopped:false},{currentDirection:null,direction:\"recvonly\",mid:null,receiver:{track:{kind:\"video\"}},sender:{track:null},stopped:false}]" but got "[]"
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/legacy/onaddstream.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/protocol/msid-parse-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/msid-parse-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webrtc/protocol/msid-parse-expected.txt
rename to third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/msid-parse-expected.txt
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/README.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/README.txt
deleted file mode 100644
index 4136332..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# This suite runs external/wpt/webrtc/ with
-# --enable-features=RTCUnifiedPlanByDefault.
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
deleted file mode 100644
index 5bd1585..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-insertDTMF.https-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS insertDTMF() should succeed if tones contains valid DTMF characters
-PASS insertDTMF() should throw InvalidCharacterError if tones contains invalid DTMF characters
-FAIL insertDTMF() should throw InvalidStateError if transceiver is stopped transceiver.stop is not a function
-PASS insertDTMF() should throw InvalidStateError if transceiver.currentDirection is recvonly
-PASS insertDTMF() should throw InvalidStateError if transceiver.currentDirection is inactive
-PASS insertDTMF() should set toneBuffer to provided tones normalized, with old tones overridden
-PASS insertDTMF() after remove and close should reject
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
deleted file mode 100644
index 4294876f..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDtlsTransport-state-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS DTLS transport goes to connected state
-FAIL close() causes the local transport to close immediately assert_equals: expected "closed" but got "connected"
-PASS close() causes the other end's DTLS transport to close
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
deleted file mode 100644
index db32f43..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-PASS addTrack when pc is closed should throw InvalidStateError
-PASS addTrack with single track argument and no stream should succeed
-PASS addTrack with single track argument and single stream should succeed
-PASS addTrack with single track argument and multiple streams should succeed
-PASS Adding the same track multiple times should throw InvalidAccessError
-PASS addTrack with existing sender with null track, same kind, and recvonly direction should reuse sender
-PASS addTrack with existing sender that has not been used to send should reuse the sender
-PASS addTrack with existing sender that has been used to send should create new sender
-PASS addTrack with existing sender with null track, different kind, and recvonly direction should create new sender
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
deleted file mode 100644
index c12b32d6..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTransceiver.https-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This is a testharness.js-based test.
-PASS addTransceiver() with string argument as invalid kind should throw TypeError
-PASS addTransceiver('audio') should return an audio transceiver
-PASS addTransceiver('video') should return a video transceiver
-PASS addTransceiver() with direction sendonly should have result transceiver.direction be the same
-PASS addTransceiver() with direction inactive should have result transceiver.direction be the same
-PASS addTransceiver() with invalid direction should throw TypeError
-PASS addTransceiver(track) should have result with sender.track be given track
-PASS addTransceiver(track) multiple times should create multiple transceivers
-FAIL addTransceiver() with rid containing invalid non-alphanumeric characters should throw TypeError assert_throws: function "() =>
-      pc.addTransceiver('audio', {
-        sendEncodings: [{
-          rid: '@Invalid!'
-        }]
-      })" did not throw
-FAIL addTransceiver() with rid longer than 16 characters should throw TypeError assert_throws: function "() =>
-      pc.addTransceiver('audio', {
-        sendEncodings: [{
-          rid: 'a'.repeat(17)
-        }]
-      })" did not throw
-PASS addTransceiver() with valid rid value should succeed
-PASS addTransceiver() with valid sendEncodings should succeed
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
deleted file mode 100644
index 8d98715..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This is a testharness.js-based test.
-PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
-FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-PASS offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists
-FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
-FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
-FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-PASS offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists
-FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
-FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
deleted file mode 100644
index 14a75b0..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-getStats.https-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a testharness.js-based test.
-PASS getStats() with no argument should succeed
-PASS getStats(null) should succeed
-PASS getStats() with track not added to connection should reject with InvalidAccessError
-PASS getStats() with track added via addTrack should succeed
-PASS getStats() with track added via addTransceiver should succeed
-PASS getStats() with track associated with more than one sender should reject with InvalidAccessError
-PASS getStats() with track associated with both sender and receiver should reject with InvalidAccessError
-PASS getStats() with no argument should return stats report containing peer-connection stats on an empty PC
-FAIL getStats() with no argument should return stats report containing peer-connection stats and outbound-track-stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
-FAIL getStats() with no argument should return stats for no-stream tracks assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
-FAIL getStats() on track associated with RtpSender should return stats report containing outbound-rtp stats assert_true: Expect statsReport to contain stats object of type outbound-rtp expected true got false
-FAIL getStats() on track associated with RtpReceiver should return stats report containing inbound-rtp stats assert_true: Expect statsReport to contain stats object of type inbound-rtp expected true got false
-FAIL getStats() with connected peer connections having tracks and data channel should return all mandatory to implement stats assert_unreached: test failed with error: Error: assert_true: Expect dictionary.dataChannelIdentifier to be integer expected true got false Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
deleted file mode 100644
index 0861101..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS Creating first data channel should fire negotiationneeded event
-PASS calling createDataChannel twice should fire negotiationneeded event once
-PASS addTransceiver() should fire negotiationneeded event
-FAIL Calling addTransceiver() twice should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is fulfilled with: [object Object] Reached unreachable code
-FAIL Calling both addTransceiver() and createDataChannel() should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is fulfilled with: [object Object] Reached unreachable code
-PASS negotiationneeded event should not fire if signaling state is not stable
-FAIL negotiationneeded event should fire only after signaling state go back to stable assert_unreached: Expect negotiationneeded promise to resolve after pc has set remote answer and go back to stable state Reached unreachable code
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
deleted file mode 100644
index 8037f38..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS setRemoteDescription should trigger ontrack event when the MSID of the stream is is parsed.
-PASS setRemoteDescription() with m= line of recvonly direction should not trigger track event
-PASS addTrack() should cause remote connection to fire ontrack when setRemoteDescription()
-PASS addTransceiver('video') should cause remote connection to fire ontrack when setRemoteDescription()
-PASS addTransceiver() with inactive direction should not cause remote connection to fire ontrack when setRemoteDescription()
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
deleted file mode 100644
index e5c29e2c..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-removeTrack.https-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS addTransceiver - Calling removeTrack when connection is closed should throw InvalidStateError
-PASS addTrack - Calling removeTrack when connection is closed should throw InvalidStateError
-PASS addTransceiver - Calling removeTrack on different connection that is closed should throw InvalidStateError
-PASS addTrack - Calling removeTrack on different connection that is closed should throw InvalidStateError
-PASS addTransceiver - Calling removeTrack on different connection should throw InvalidAccessError
-PASS addTrack - Calling removeTrack on different connection should throw InvalidAccessError
-PASS addTransceiver - Calling removeTrack with valid sender should set sender.track to null
-PASS addTrack - Calling removeTrack with valid sender should set sender.track to null
-PASS Calling removeTrack with currentDirection sendrecv should set direction to recvonly
-PASS Calling removeTrack with currentDirection sendonly should set direction to inactive
-PASS Calling removeTrack with currentDirection recvonly should not change direction
-PASS Calling removeTrack with currentDirection inactive should not change direction
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
deleted file mode 100644
index f3a08e1..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setDescription-transceiver-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS setLocalDescription(offer) with m= section should assign mid to corresponding transceiver
-PASS setRemoteDescription(offer) with m= section and no existing transceiver should create corresponding transceiver
-FAIL setLocalDescription(rollback) should unset transceiver.mid promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
-FAIL setLocalDescription(rollback) should only unset transceiver mids associated with current round promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
-FAIL setRemoteDescription(rollback) should remove newly created transceiver from transceiver list promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': The provided value 'rollback' is not a valid enum value of type RTCSdpType."
-FAIL setRemoteDescription should stop the transceiver if its corresponding m section is rejected promise_test: Unhandled rejection with value: object "TypeError: pc2.getTransceivers(...)[0].stop is not a function"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-expected.txt
deleted file mode 100644
index 04c8163..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS setRemoteDescription with invalid type and invalid SDP should reject with TypeError
-FAIL setRemoteDescription() with invalid SDP and stable state should reject with InvalidStateError assert_throws: function "() => { throw e }" threw object "OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. invalid Expect line: v=" that is not a DOMException InvalidStateError: property "code" is equal to 0, expected 11
-PASS Negotiation should fire signalingsstate events
-PASS Calling setRemoteDescription() again after one round of remote-offer/local-answer should succeed
-PASS Switching role from offerer to answerer after going back to stable state should succeed
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
deleted file mode 100644
index 900f0f73..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-replaceTrack.https-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS replaceTrack() sets the track attribute to a new track.
-PASS replaceTrack() sets the track attribute to null.
-PASS replaceTrack() does not set the track synchronously.
-PASS replaceTrack() rejects when the peer connection is closed.
-PASS replaceTrack() does not reject when invoked after removeTrack().
-PASS replaceTrack() does not reject after a subsequent removeTrack().
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
deleted file mode 100644
index 0505a96f..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-PASS addTrack() with a track and no stream makes ontrack fire with a track and no stream.
-PASS addTrack() with a track and a stream makes ontrack fire with a track and a stream.
-PASS ontrack fires before setRemoteDescription resolves.
-PASS addTrack() with two tracks and one stream makes ontrack fire twice with the tracks and shared stream.
-PASS addTrack() for an existing stream makes stream.onaddtrack fire.
-PASS stream.onaddtrack fires before setRemoteDescription resolves.
-PASS addTrack() with a track and two streams makes ontrack fire with a track and two streams.
-PASS ontrack's receiver matches getReceivers().
-PASS removeTrack() does not remove the receiver.
-PASS removeTrack() makes stream.onremovetrack fire and the track to be removed from the stream.
-PASS stream.onremovetrack fires before setRemoteDescription resolves.
-PASS removeTrack() makes track.onmute fire and the track to be muted.
-PASS track.onmute fires before setRemoteDescription resolves.
-PASS removeTrack() twice is safe.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
deleted file mode 100644
index 90cc799..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-transceivers.https-expected.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-This is a testharness.js-based test.
-PASS addTrack: creates a transceiver for the sender
-PASS addTrack: "transceiver == {sender,receiver}"
-PASS addTrack: transceiver.sender is associated with the track
-PASS addTrack: transceiver.receiver has its own track
-PASS addTrack: transceiver.receiver's track is muted
-PASS addTrack: transceiver is not associated with an m-section
-PASS addTrack: transceiver is not stopped
-PASS addTrack: transceiver's direction is sendrecv
-PASS addTrack: transceiver's currentDirection is null
-PASS setLocalDescription(offer): transceiver gets associated with an m-section
-PASS setLocalDescription(offer): transceiver.mid matches the offer SDP
-PASS setRemoteDescription(offer): ontrack fires with a track
-PASS setRemoteDescription(offer): ontrack's track.id is the same as track.id
-PASS setRemoteDescription(offer): ontrack fires with a transceiver.
-PASS setRemoteDescription(offer): transceiver.mid is the same on both ends
-PASS setRemoteDescription(offer): "transceiver == {sender,receiver}"
-PASS setRemoteDescription(offer): transceiver.direction is recvonly
-PASS setRemoteDescription(offer): transceiver.currentDirection is null
-PASS setRemoteDescription(offer): transceiver.stopped is false
-PASS setLocalDescription(answer): transceiver.currentDirection is recvonly
-PASS setLocalDescription(answer): transceiver.currentDirection is sendonly
-PASS addTransceiver(track): creates a transceiver for the track
-PASS addTransceiver(track): "transceiver == {sender,receiver}"
-PASS addTransceiver(track, init): initialize direction to inactive
-PASS addTransceiver(track, init): initialize sendEncodings[0].active to false
-PASS addTransceiver(0 streams): ontrack fires with no stream
-PASS addTransceiver(1 stream): ontrack fires with corresponding stream
-PASS addTransceiver(2 streams): ontrack fires with corresponding two streams
-PASS addTrack(0 streams): ontrack fires with no stream
-PASS addTrack(1 stream): ontrack fires with corresponding stream
-PASS addTrack(2 streams): ontrack fires with corresponding two streams
-PASS addTransceiver('audio'): creates a transceiver with direction sendrecv
-PASS addTransceiver('audio'): transceiver.receiver.track.kind == 'audio'
-PASS addTransceiver('video'): transceiver.receiver.track.kind == 'video'
-PASS addTransceiver('audio'): transceiver.sender.track == null
-PASS addTransceiver('audio'): transceiver.currentDirection is null
-PASS addTransceiver('audio'): transceiver.stopped is false
-PASS addTrack reuses reusable transceivers
-PASS addTransceiver does not reuse reusable transceivers
-PASS Can setup two-way call using a single transceiver
-PASS Closing the PC stops the transceivers
-PASS Changing transceiver direction to 'sendrecv' makes ontrack fire
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
deleted file mode 100644
index 2a977b0..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-codecs-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS setParameters() with codec.payloadType modified should reject with InvalidModificationError
-PASS setParameters() with codec.mimeType modified should reject with InvalidModificationError
-PASS setParameters() with codec.clockRate modified should reject with InvalidModificationError
-PASS setParameters() with codec.channels modified should reject with InvalidModificationError
-PASS setParameters() with codec.sdpFmtpLine modified should reject with InvalidModificationError
-PASS setParameters() with new codecs inserted should reject with InvalidModificationError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
deleted file mode 100644
index 57b1f47..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-degradationPreference-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters with degradationPreference set should succeed assert_equals: Expect initial param.degradationPreference to be balanced expected (string) "balanced" but got (undefined) undefined
-FAIL setParameters with degradationPreference unset should succeed assert_equals: Expect initial param.degradationPreference to be balanced expected (string) "balanced" but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
deleted file mode 100644
index 3ce1fdd6..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-encodings-expected.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is a testharness.js-based test.
-PASS addTransceiver() with undefined sendEncodings should have default encoding parameter with active set to true
-PASS addTransceiver() with empty list sendEncodings should have default encoding parameter with active set to true
-FAIL sender.getParameters() should return sendEncodings set by addTransceiver() promise_test: Unhandled rejection with value: object "InvalidAccessError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Failed to set remote audio description send parameters."
-PASS sender.setParameters() with mismatch number of encodings should reject with InvalidModificationError
-PASS sender.setParameters() with encodings unset should reject with TypeError
-FAIL setParameters() with modified encoding.rid field should reject with InvalidModificationError assert_equals: expected (string) "foo" but got (undefined) undefined
-FAIL setParameters() with encoding.scaleResolutionDownBy field set to less than 1.0 should reject with RangeError assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL setParameters() with encoding.scaleResolutionDownBy field set to greater than 1.0 should succeed assert_approx_equals: expected a number but got a "undefined"
-FAIL setParameters() with modified encoding.dtx should succeed with RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
-FAIL setParameters() with modified encoding.dtx should succeed without RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
-FAIL setParameters() with unset encoding.dtx should succeed with RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
-FAIL setParameters() with unset encoding.dtx should succeed without RTCRtpTransceiverInit assert_equals: expected (string) "enabled" but got (undefined) undefined
-PASS setParameters() with modified encoding.active should succeed with RTCRtpTransceiverInit
-PASS setParameters() with modified encoding.active should succeed without RTCRtpTransceiverInit
-PASS setParameters() with modified encoding.priority should succeed with RTCRtpTransceiverInit
-PASS setParameters() with modified encoding.priority should succeed without RTCRtpTransceiverInit
-PASS setParameters() with modified encoding.networkPriority should succeed with RTCRtpTransceiverInit
-PASS setParameters() with modified encoding.networkPriority should succeed without RTCRtpTransceiverInit
-FAIL setParameters() with modified encoding.ptime should succeed with RTCRtpTransceiverInit assert_equals: expected (number) 2 but got (undefined) undefined
-FAIL setParameters() with modified encoding.ptime should succeed without RTCRtpTransceiverInit assert_equals: expected (number) 2 but got (undefined) undefined
-PASS setParameters() with modified encoding.maxBitrate should succeed with RTCRtpTransceiverInit
-PASS setParameters() with modified encoding.maxBitrate should succeed without RTCRtpTransceiverInit
-FAIL setParameters() with modified encoding.maxFramerate should succeed with RTCRtpTransceiverInit assert_equals: expected (number) 24 but got (undefined) undefined
-FAIL setParameters() with modified encoding.maxFramerate should succeed without RTCRtpTransceiverInit assert_equals: expected (number) 24 but got (undefined) undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
deleted file mode 100644
index b3fae21..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-headerExtensions-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-PASS setParameters() with modified headerExtensions should reject with InvalidModificationError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
deleted file mode 100644
index d537b97..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-rtcp-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS setParameters() with modified rtcp.cname should reject with InvalidModificationError
-PASS setParameters() with modified rtcp.reducedSize should reject with InvalidModificationError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
deleted file mode 100644
index cda6ce89..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpParameters-transactionId-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS sender.getParameters() should return different transaction IDs for each call
-PASS sender.setParameters() with transaction ID different from last getParameters() should reject with InvalidModificationError
-PASS sender.setParameters() with transaction ID unset should reject with TypeError
-PASS setParameters() twice with the same parameters should reject with InvalidStateError
-PASS setParameters() with parameters older than last getParameters() should reject with InvalidModificationError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
deleted file mode 100644
index 4f8f48aa..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getParameters-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS getParameters() with audio receiver
-PASS getParameters() with video receiver
-FAIL getParameters() with simulcast video receiver promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': Attempted to create an encoder with more than 1 encoding parameter."
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
deleted file mode 100644
index 002ae802..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpReceiver-getStats.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL receiver.getStats() via addTransceiver should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
-FAIL receiver.getStats() via addTrack should return stats report containing inbound-rtp stats assert_equals: Expect dictionary.codecId to be string expected "string" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
deleted file mode 100644
index f7302af8..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-getStats.https-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL sender.getStats() via addTransceiver should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
-FAIL sender.getStats() via addTrack should return stats report containing outbound-rtp stats assert_equals: Expect dictionary.remoteId to be string expected "string" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
deleted file mode 100644
index 8b4a12b3..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-replaceTrack.https-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-PASS Calling replaceTrack on closed connection should reject with InvalidStateError
-FAIL Calling replaceTrack with track of different kind should reject with TypeError assert_throws: function "function() { throw e }" threw object "InvalidModificationError" ("InvalidModificationError") expected object "TypeError" ("TypeError")
-FAIL Calling replaceTrack on stopped sender should reject with InvalidStateError promise_test: Unhandled rejection with value: object "TypeError: transceiver.stop is not a function"
-PASS Calling replaceTrack on sender with null track and not set to session description should resolve with sender.track set to given track
-PASS Calling replaceTrack on sender not set to session description should resolve with sender.track set to given track
-PASS Calling replaceTrack(null) on sender not set to session description should resolve with sender.track set to null
-PASS Calling replaceTrack(null) on sender set to session description should resolve with sender.track set to null
-PASS Calling replaceTrack on sender with stopped track and and set to session description should resolve with sender.track set to given track
-PASS Calling replaceTrack on sender with similar track and and set to session description should resolve with sender.track set to new track
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
deleted file mode 100644
index 2fc9877..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-setParameters-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL setParameters() when transceiver is stopped should reject with InvalidStateError transceiver.stop is not a function
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
deleted file mode 100644
index 8577f12..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpSender-transport.https-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS RTCRtpSender.transport is null when unconnected
-PASS RTCRtpSender/receiver.transport has a value when connected
-PASS RTCRtpSender/receiver.transport at the right time, with bundle policy balanced
-PASS RTCRtpSender/receiver.transport at the right time, with bundle policy max-bundle
-PASS RTCRtpSender/receiver.transport at the right time, with bundle policy max-compat
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
deleted file mode 100644
index ef1387d..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver-direction-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS setting direction should change transceiver.direction
-PASS setting direction with same direction should have no effect
-PASS setting direction should change transceiver.direction independent of transceiver.currentDirection
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
deleted file mode 100644
index a13c414..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCRtpTransceiver-setCodecPreferences-expected.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This is a testharness.js-based test.
-FAIL setCodecPreferences() on audio transceiver with codecs returned from RTCRtpSender.getCapabilities('audio') should succeed transceiver.setCodecPreferences is not a function
-FAIL setCodecPreferences() on video transceiver with codecs returned from RTCRtpReceiver.getCapabilities('video') should succeed transceiver.setCodecPreferences is not a function
-FAIL setCodecPreferences() with both sender receiver codecs combined should succeed transceiver.setCodecPreferences is not a function
-FAIL setCodecPreferences([]) should succeed transceiver.setCodecPreferences is not a function
-FAIL setCodecPreferences() with reordered codecs should succeed transceiver.setCodecPreferences is not a function
-FAIL setCodecPreferences() on audio transceiver with codecs returned from getCapabilities('video') should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(capabilities.codecs)" passed to assert_throws()
-FAIL setCodecPreferences() with user defined codec should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(codecs)" passed to assert_throws()
-FAIL setCodecPreferences() with user defined codec together with codecs returned from getCapabilities() should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(codecs)" passed to assert_throws()
-FAIL setCodecPreferences() with modified codecs returned from getCapabilities() should throw InvalidAccessError Test bug: unrecognized DOMException code "() => transceiver.setCodecPreferences(codecs)" passed to assert_throws()
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
deleted file mode 100644
index 7b3d87c..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCTrackEvent-constructor-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-FAIL new RTCTrackEvent() with valid receiver, track, transceiver should succeed assert_equals: expected [] but got []
-PASS new RTCTrackEvent() with valid receiver, track, streams, transceiver should succeed
-PASS new RTCTrackEvent() with valid receiver, track, multiple streams, transceiver should succeed
-PASS new RTCTrackEvent() with unrelated receiver, track, streams, transceiver should succeed
-PASS new RTCTrackEvent() with no transceiver should throw TypeError
-PASS new RTCTrackEvent() with no track should throw TypeError
-PASS new RTCTrackEvent() with no receiver should throw TypeError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/interfaces.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/interfaces.https-expected.txt
deleted file mode 100644
index 090c43b..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/interfaces.https-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS Main test driver
-PASS Test driver for asyncInitCertificate
-FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
-PASS Test driver for asyncInitMediaStreamTrack
-FAIL EventTarget interface: existence and properties of interface object assert_throws: interface object didn't throw TypeError when called as a constructor function "function () { [native code] }" did not throw
-PASS EventTarget interface object length
-PASS EventTarget interface object name
-PASS EventTarget interface: existence and properties of interface prototype object
-PASS EventTarget interface: existence and properties of interface prototype object's "constructor" property
-PASS EventTarget interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaStreamTrack interface: existence and properties of interface object
-PASS MediaStreamTrack interface object length
-PASS MediaStreamTrack interface object name
-PASS MediaStreamTrack interface: existence and properties of interface prototype object
-PASS MediaStreamTrack interface: existence and properties of interface prototype object's "constructor" property
-PASS MediaStreamTrack interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaStreamTrack must be primary interface of idlTestObjects.mediaStreamTrack
-PASS Stringification of idlTestObjects.mediaStreamTrack
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
deleted file mode 100644
index 9714f8a..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/RTCPeerConnection-createOffer-offerToReceive-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-This is a testharness.js-based test.
-PASS createOffer() with offerToReceiveAudio should add audio line to all subsequent created offers
-PASS createOffer() with offerToReceiveVideo should add video line to all subsequent created offers
-PASS createOffer() with offerToReceiveAudio:true, then with offerToReceiveVideo:true, should have result offer with both audio and video line
-PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
-FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-PASS offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists
-FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
-FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
-FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
-PASS offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists
-FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" assert_equals: Expect transceiver to have "inactive" direction expected "inactive" but got "recvonly"
-FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
-FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/onaddstream.https-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
deleted file mode 100644
index 6742df7..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/legacy/onaddstream.https-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-PASS Check onaddstream
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/protocol/msid-parse-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/protocol/msid-parse-expected.txt
deleted file mode 100644
index 90fdfa4..0000000
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/protocol/msid-parse-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-PASS Description with no msid produces a track with a stream
-PASS Description with msid:- appid produces a track with no stream
-PASS Description with msid:foo bar produces a stream with id foo
-PASS Description with two msid produces two streams
-Harness: the test ran to completion.
-
diff --git a/tools/grit/grit/format/resource_map.py b/tools/grit/grit/format/resource_map.py
index b9c0130..3f4ab90 100644
--- a/tools/grit/grit/format/resource_map.py
+++ b/tools/grit/grit/format/resource_map.py
@@ -142,11 +142,30 @@
 def _GetItemName(item):
   return item.attrs['name']
 
+# Check if |path2| is a subpath of |path1|.
+def _IsSubpath(path1, path2):
+  path1_abs = os.path.abspath(path1)
+  path2_abs = os.path.abspath(path2)
+  common = os.path.commonprefix([path1_abs, path2_abs])
+  return path1_abs == common
 
 def _GetItemPath(item):
   path = item.GetInputPath().replace("\\", "/")
-  # resource_maps don't currently work with variables. See crbug.com/899437
-  # for why you might not need this, and if you still think you need it then
-  # comment 17 has a patch for how to make it work.
-  assert '$' not in path, 'see comment above'
+
+  # Handle the case where the file resides within the output folder,
+  # by expanding any variables as well as replacing the output folder name with
+  # a fixed string such that the key added to the map does not depend on a given
+  # developer's setup.
+  #
+  # For example this will convert the following path:
+  # ../../out/gchrome/${root_gen_dir}/ui/webui/resources/js/foo.js
+  # to:
+  # @out_folder@/gen/ui/webui/resources/js/foo.js
+
+  real_path = item.ToRealPath(item.GetInputPath())
+  if (item.attrs.get('use_base_dir', 'true') != 'true' and
+          _IsSubpath(os.path.curdir, real_path)):
+    path = os.path.join('@out_folder@', os.path.relpath(real_path))
+
+  assert '$' not in path, 'all variables should have been expanded'
   return path
diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
index 74ea11a..8ec788b 100755
--- a/tools/grit/grit/format/resource_map_unittest.py
+++ b/tools/grit/grit/format/resource_map_unittest.py
@@ -88,6 +88,38 @@
 };
 const size_t kTheRcHeaderSize = base::size(kTheRcHeader);''', output)
 
+  def testGzippedMapFileSourceWithGeneratedFile(self):
+    os.environ["root_gen_dir"] = "gen"
+
+    grd = util.ParseGrdForUnittest('''\
+        <outputs>
+          <output type="rc_header" filename="the_rc_header.h" />
+          <output type="gzipped_resource_map_header"
+                  filename="gzipped_resource_map_header.h" />
+        </outputs>
+        <release seq="3">
+          <includes first_id="10000">
+            <include type="BINDATA"
+                     file="${root_gen_dir}/foo/bar/baz.js"
+                     name="IDR_FOO_BAR_BAZ_JS"
+                     use_base_dir="false"
+                     compress="gzip" />
+         </includes>
+        </release>''', run_gatherers=True)
+
+    formatter = resource_map.GetFormatter('gzipped_resource_file_map_source')
+    output = util.StripBlankLinesAndComments(''.join(formatter(grd, 'en', '.')))
+    expected = '''\
+#include "gzipped_resource_map_header.h"
+#include <stddef.h>
+#include "base/stl_util.h"
+#include "the_rc_header.h"
+const GzippedGritResourceMap kTheRcHeader[] = {
+  {"@out_folder@/gen/foo/bar/baz.js", IDR_FOO_BAR_BAZ_JS, true},
+};
+const size_t kTheRcHeaderSize = base::size(kTheRcHeader);'''
+    self.assertEqual(expected, output)
+
   def testGzippedMapHeaderAndFileSource(self):
     grd = util.ParseGrdForUnittest('''\
         <outputs>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f058f67..9ad0783 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -22587,6 +22587,11 @@
   <int value="642" label="NSFileWriteVolumeReadOnlyError"/>
 </enum>
 
+<enum name="FramebustInterventionOutcome">
+  <int value="0" label="Accepted"/>
+  <int value="1" label="Declined and navigated"/>
+</enum>
+
 <enum name="FramebustPermissions">
   <int value="0"
       label="Only permitted for framebusting, no user gesture, not an ad
@@ -37972,6 +37977,7 @@
   <int value="3" label="Parsing failed"/>
   <int value="4" label="Decoding failed"/>
   <int value="5" label="Logo revalidated"/>
+  <int value="6" label="Missing image"/>
 </enum>
 
 <enum name="NewTabPageLogoShown">
@@ -40961,27 +40967,18 @@
 </enum>
 
 <enum name="PasswordManagerFillingAssistance">
-  <summary>
-    This enum shows the user experience with the passwords filling. The first 4
-    buckets are ranging from the best (automatic) to the worst (the user has to
-    type already saved password). Also there are buckets that showed the cases
-    when it was impossible to help because the unknown credentials were
-    submitted. The last bucket is correspond to the strange cases, that the
-    submitted form has nor user input, nor autofilled data in password fields.
-  </summary>
-  <int value="0" label="Credential fields were filled automatically"/>
+  <int value="0" label=":-D Credential fields filled automatically"/>
   <int value="1"
-      label="Credential fields were filled by manual filling (but none
-             required typing)"/>
+      label=":-) Credential fields filled manually (but none required typing)"/>
   <int value="2"
-      label="Password was filled (automatically or manually), known username
+      label=":-| Password filled (automatically or manually), known username
              was typed"/>
-  <int value="3" label="Known password was typed"/>
+  <int value="3" label=":-( Saved password was typed"/>
   <int value="4"
-      label="Unknown credentials were typed (neither username nor password
-             were known) while some credentials were stored."/>
-  <int value="5" label="No saved credentials"/>
-  <int value="6" label="Neither user input nor filling in password fields">
+      label=":-X Unknown password typed, some saved credentials existed"/>
+  <int value="5"
+      label=":-X Unknown password typed, no saved credentials existed"/>
+  <int value="6" label="%-) Neither filling nor user input">
     For example, it might be because of 3rd party password managers.
   </int>
 </enum>
@@ -45769,6 +45766,12 @@
   <int value="3" label="Stage 3 Failure"/>
 </enum>
 
+<enum name="Rollback_RollbackSaveResult">
+  <int value="0" label="Succeeded"/>
+  <int value="1" label="Stage 1 Failure"/>
+  <int value="2" label="Stage 2 Failure"/>
+</enum>
+
 <enum name="RSAKeyUsage">
   <int value="0" label="Not an RSA key"/>
   <int value="1" label="OK (no extension)"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 527f8429..8067dd1 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -29050,6 +29050,9 @@
 
 <histogram name="Event.Latency.ScrollBegin.Touch.TimeToScrollUpdateSwapBegin2"
     units="microseconds">
+  <obsolete>
+    Replaced by *.TimeToScrollUpdateSwapBegin4 https://crbug.com/849735 in M68.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initial creation of a touch event and the start of the frame
@@ -29196,6 +29199,9 @@
 
 <histogram name="Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin2"
     units="microseconds">
+  <obsolete>
+    Replaced by *.TimeToScrollUpdateSwapBegin4 https://crbug.com/849735 in M68.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel event and the start of the frame
@@ -29321,6 +29327,9 @@
 <histogram
     name="Event.Latency.ScrollInertial.Touch.TimeToScrollUpdateSwapBegin2"
     units="microseconds">
+  <obsolete>
+    Replaced by *.TimeToScrollUpdateSwapBegin4 https://crbug.com/849735 in M68.
+  </obsolete>
   <owner>sahel@chromium.org</owner>
   <summary>
     Time between initial creation of a ScrollUpdate gesture event generated from
@@ -29563,6 +29572,9 @@
 
 <histogram name="Event.Latency.ScrollUpdate.Touch.TimeToScrollUpdateSwapBegin2"
     units="microseconds">
+  <obsolete>
+    Replaced by *.TimeToScrollUpdateSwapBegin4 https://crbug.com/849735 in M68.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initial creation of a touch event and start of the frame swap
@@ -29742,6 +29754,9 @@
 
 <histogram name="Event.Latency.ScrollUpdate.Wheel.TimeToScrollUpdateSwapBegin2"
     units="microseconds">
+  <obsolete>
+    Replaced by *.TimeToScrollUpdateSwapBegin4 https://crbug.com/849735 in M68.
+  </obsolete>
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel event and start of the frame swap
@@ -79074,12 +79089,26 @@
 </histogram>
 
 <histogram name="PasswordManager.FillingAssistance"
-    enum="PasswordManagerFillingAssistance">
+    enum="PasswordManagerFillingAssistance" expires_after="2020-02-01">
   <owner>dvadym@chromium.org</owner>
   <owner>battre@chromium.org</owner>
   <summary>
-    This metric records the user experience with the passwords filling. Is is
-    recorded on password form submissions that are considered to be successful.
+    This metric records the user experience with regards to passwords filling.
+    It is only recorded on password form submissions that are considered to be
+    successful.
+
+    The first 4 buckets (0..3) are ranging from the best filling behavior
+    (automatic filling on page load without user interaction necessary) to the
+    worst (the user had to manually type a password that was already saved).
+
+    The following buckets (4..5) indicate cases when it was impossible to fill
+    credentials because unknown credentials were submitted, meaning that the
+    submitted password was not saved before.
+
+    The last bucket (6) corresponds to the strange cases, where the submitted
+    form has neither user input nor autofilled data in password fields. This
+    might indicate third-party password manager use, or might be a
+    measurement/submission classification error.
   </summary>
 </histogram>
 
@@ -94199,6 +94228,14 @@
   </summary>
 </histogram>
 
+<histogram name="Rollback.RollbackSaveResult"
+    enum="Rollback_RollbackSaveResult">
+  <owner>zentaro@chromium.org</owner>
+  <summary>
+    The result when attempting to save data during a Chrome OS rollback.
+  </summary>
+</histogram>
+
 <histogram name="SadTab.Created" enum="SadTabKind">
   <obsolete>
     Replaced with Tabs.SadTab.* in R20.
@@ -124190,6 +124227,30 @@
   </summary>
 </histogram>
 
+<histogram name="WebCore.Framebust.ClickThroughPosition"
+    enum="ListItemPosition" expires_after="2020-01-30">
+  <owner>japhet@chromium.org</owner>
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    The position of the URL in the framebust UI list (desktop only) when it is
+    clicked. Note that this UI surface is shared with the tab-under
+    intervention, so elements in the list could come from either features. This
+    metric will only be logged when a URL from the framebust (3p redirect)
+    intervention is clicked.
+  </summary>
+</histogram>
+
+<histogram name="WebCore.Framebust.InterventionOutcome"
+    enum="FramebustInterventionOutcome" expires_after="2020-01-30">
+  <owner>japhet@chromium.org</owner>
+  <owner>csharrison@chromium.org</owner>
+  <summary>
+    The (android only) outcome of a particular framebust infobar. Recorded when
+    the user either closes the infobar or declines the intervention by clicking
+    the link.
+  </summary>
+</histogram>
+
 <histogram name="WebCore.Framebust2" enum="FramebustPermissionsPlusAds">
   <owner>japhet@chromium.org, jkarlin@chromium.org</owner>
   <summary>
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index c208cbb..31d184b7 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -254,6 +254,8 @@
 crbug.com/903417 [ Mac ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
 crbug.com/903417 [ Win ] system_health.common_desktop/long_running:tools:gmail-foreground [ Skip ]
 crbug.com/911214 [ Win_10 ] system_health.common_desktop/multitab:misc:typical24 [ Skip ]
+crbug.com/923106 [ Linux ] system_health.common_desktop/browse:media:tumblr [ Skip ]
+crbug.com/923106 [ Win ] system_health.common_desktop/browse:media:tumblr [ Skip ]
 
 # Benchmark: system_health.common_mobile
 crbug.com/914390 [ Nexus_5 ] system_health.common_mobile/browse:chrome:newtab [ Skip ]
@@ -266,6 +268,7 @@
 crbug.com/877648 [ Android ] system_health.common_mobile/long_running:tools:gmail-foreground [ Skip ]
 crbug.com/896851 [ Android ] system_health.common_mobile/background:tools:gmail [ Skip ]
 crbug.com/896871 [ Android ] system_health.common_mobile/load:tools:gmail [ Skip ]
+crbug.com/923116 [ Android ] system_health.common_mobile/browse:shopping:avito [ Skip ]
 
 # Benchmark: system_health.memory_desktop
 crbug.com/728576 [ Mac ] system_health.memory_desktop/browse:news:cnn [ Skip ]
@@ -318,6 +321,7 @@
 crbug.com/883320 [ Android_Go ] system_health.memory_mobile/browse:social:tumblr_infinite_scroll [ Skip ]
 crbug.com/880652 [ Nexus_5X ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
 crbug.com/883652 [ Nexus_5 ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
+crbug.com/923116 [ Android ] system_health.memory_mobile/browse:shopping:avito [ Skip ]
 crbug.com/893873 [ Nexus_5X ] system_health.memory_mobile/load:news:washingtonpost [ Skip ]
 crbug.com/896851 [ Android ] system_health.memory_mobile/background:tools:gmail [ Skip ]
 crbug.com/892704 [ Android_Webview ] system_health.memory_mobile/browse:tech:discourse_infinite_scroll:2018 [ Skip ]
@@ -379,6 +383,7 @@
 crbug.com/877648 [ Android_Go ] v8.browsing_mobile/browse:news:toi [ Skip ]
 crbug.com/877648 [ Android_Go ] v8.browsing_mobile/browse:news:cnn [ Skip ]
 crbug.com/901967 [ Nexus6_Webview ] v8.browsing_mobile/browse:news:toi [ Skip ]
+crbug.com/923116 [ Android ] v8.browsing_mobile/browse:shopping:avito [ Skip ]
 
 # Benchmark: v8.browsing_mobile-future
 crbug.com/714650 [ Android ] v8.browsing_mobile-future/browse:news:globo [ Skip ]
@@ -390,6 +395,7 @@
 crbug.com/799080 [ Nexus_5X Android_Webview ] v8.browsing_mobile-future/browse:social:facebook [ Skip ]
 crbug.com/901534 [ Nexus6_Webview ] v8.browsing_mobile-future/browse:news:toi [ Skip ]
 crbug.com/865400 [ Pixel_2 ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
+crbug.com/923116 [ Android ] v8.browsing_mobile-future/browse:shopping:avito [ Skip ]
 crbug.com/906654 [ All ] v8.browsing_mobile-future/browse:tech:discourse_infinite_scroll [ Skip ]
 crbug.com/906654 [ All ] v8.browsing_mobile-future/browse:social:facebook_infinite_scroll [ Skip ]
 
diff --git a/tools/win/DebugVisualizers/webkit.natvis b/tools/win/DebugVisualizers/webkit.natvis
index 6b1f0235..16a45a099 100644
--- a/tools/win/DebugVisualizers/webkit.natvis
+++ b/tools/win/DebugVisualizers/webkit.natvis
@@ -237,7 +237,37 @@
   <Type Name="blink::NGPhysicalFragment">
     <DisplayString>{(blink::NGPhysicalFragment::NGFragmentType)type_} {size_} {*layout_object_}</DisplayString>
     <Expand>
-      <Item Name="children_" Condition="type_ == 0 || type_ == 2">((blink::NGPhysicalContainerFragment*)this)->children_</Item>
+      <!-- Resolve subclasses because VS can't resolve without virtual functions -->
+      <ExpandedItem Condition="type_ == 0">(blink::NGPhysicalBoxFragment*)this</ExpandedItem>
+      <ExpandedItem Condition="type_ == 1">(blink::NGPhysicalTextFragment*)this</ExpandedItem>
+      <ExpandedItem Condition="type_ == 2">(blink::NGPhysicalLineBoxFragment*)this</ExpandedItem>
+    </Expand>
+  </Type>
+  <Type Name="blink::NGPhysicalBoxFragment">
+    <Expand>
+      <Item Name="SubType">(blink::NGPhysicalFragment::NGBoxType)sub_type_</Item>
+      <Synthetic Name="Children">
+        <DisplayString>{num_children_}</DisplayString>
+        <Expand>
+          <ArrayItems>
+            <Size>num_children_</Size>
+            <ValuePointer>children_</ValuePointer>
+          </ArrayItems>
+        </Expand>
+      </Synthetic>
+    </Expand>
+  </Type>
+  <Type Name="blink::NGPhysicalLineBoxFragment">
+    <Expand>
+      <Synthetic Name="Children">
+        <DisplayString>{num_children_}</DisplayString>
+        <Expand>
+          <ArrayItems>
+            <Size>num_children_</Size>
+            <ValuePointer>children_</ValuePointer>
+          </ArrayItems>
+        </Expand>
+      </Synthetic>
     </Expand>
   </Type>
   <Type Name="blink::NGLogicalOffset">
diff --git a/ui/accessibility/ax_event_generator_unittest.cc b/ui/accessibility/ax_event_generator_unittest.cc
index 923b163..6b17f7b 100644
--- a/ui/accessibility/ax_event_generator_unittest.cc
+++ b/ui/accessibility/ax_event_generator_unittest.cc
@@ -831,4 +831,39 @@
       DumpEvents(&event_generator));
 }
 
+TEST(AXEventGeneratorTest, ActiveDescendantChangeOnDescendant) {
+  AXTreeUpdate initial_state;
+  initial_state.root_id = 1;
+  initial_state.nodes.resize(5);
+  initial_state.nodes[0].id = 1;
+  initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
+  initial_state.nodes[0].child_ids.push_back(2);
+  initial_state.nodes[1].id = 2;
+  initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer;
+  initial_state.nodes[1].child_ids.push_back(3);
+  initial_state.nodes[2].id = 3;
+  initial_state.nodes[2].role = ax::mojom::Role::kGroup;
+  initial_state.nodes[2].AddIntAttribute(
+      ax::mojom::IntAttribute::kActivedescendantId, 4);
+  initial_state.nodes[2].child_ids.push_back(4);
+  initial_state.nodes[2].child_ids.push_back(5);
+  initial_state.nodes[3].id = 4;
+  initial_state.nodes[3].role = ax::mojom::Role::kGroup;
+  initial_state.nodes[4].id = 5;
+  initial_state.nodes[4].role = ax::mojom::Role::kGroup;
+
+  AXTree tree(initial_state);
+
+  AXEventGenerator event_generator(&tree);
+  initial_state.nodes[2].RemoveIntAttribute(
+      ax::mojom::IntAttribute::kActivedescendantId);
+  initial_state.nodes[2].AddIntAttribute(
+      ax::mojom::IntAttribute::kActivedescendantId, 5);
+  AXTreeUpdate update = initial_state;
+  update.node_id_to_clear = 2;
+  EXPECT_TRUE(tree.Unserialize(update));
+  EXPECT_EQ("ACTIVE_DESCENDANT_CHANGED on 3, RELATED_NODE_CHANGED on 3",
+            DumpEvents(&event_generator));
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc
index 13e462df..b7b1928 100644
--- a/ui/accessibility/ax_node_data.cc
+++ b/ui/accessibility/ax_node_data.cc
@@ -209,6 +209,22 @@
   relative_bounds = other.relative_bounds;
 }
 
+AXNodeData::AXNodeData(AXNodeData&& other) {
+  id = other.id;
+  role = other.role;
+  state = other.state;
+  actions = other.actions;
+  string_attributes.swap(other.string_attributes);
+  int_attributes.swap(other.int_attributes);
+  float_attributes.swap(other.float_attributes);
+  bool_attributes.swap(other.bool_attributes);
+  intlist_attributes.swap(other.intlist_attributes);
+  stringlist_attributes.swap(other.stringlist_attributes);
+  html_attributes.swap(other.html_attributes);
+  child_ids.swap(other.child_ids);
+  relative_bounds = other.relative_bounds;
+}
+
 AXNodeData& AXNodeData::operator=(AXNodeData other) {
   id = other.id;
   role = other.role;
diff --git a/ui/accessibility/ax_node_data.h b/ui/accessibility/ax_node_data.h
index 5aebdf41..9762ed1b 100644
--- a/ui/accessibility/ax_node_data.h
+++ b/ui/accessibility/ax_node_data.h
@@ -37,6 +37,7 @@
   virtual ~AXNodeData();
 
   AXNodeData(const AXNodeData& other);
+  AXNodeData(AXNodeData&& other);
   AXNodeData& operator=(AXNodeData other);
 
   // Accessing accessibility attributes:
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 8e52a48..6bc5c6a 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -95,20 +95,30 @@
 struct AXTreeUpdateState {
   AXTreeUpdateState() : new_root(nullptr) {}
   // Returns whether this update changes |node|.
-  bool HasChangedNode(const AXNode* node) {
+  bool IsChangedNode(const AXNode* node) {
     return changed_node_ids.find(node->id()) != changed_node_ids.end();
   }
 
   // Returns whether this update removes |node|.
-  bool HasRemovedNode(const AXNode* node) {
+  bool IsRemovedNode(const AXNode* node) {
     return removed_node_ids.find(node->id()) != removed_node_ids.end();
   }
 
+  // Returns whether this update creates |node|.
+  bool IsNewNode(const AXNode* node) {
+    return new_nodes.find(node) != new_nodes.end();
+  }
+
+  // Returns whether this update reparents |node|.
+  bool IsReparentedNode(const AXNode* node) {
+    return IsNewNode(node) && IsRemovedNode(node);
+  }
+
   // During an update, this keeps track of all nodes that have been
   // implicitly referenced as part of this update, but haven't been
   // updated yet. It's an error if there are any pending nodes at the
   // end of Unserialize.
-  std::set<AXNode*> pending_nodes;
+  std::set<const AXNode*> pending_nodes;
 
   // This is similar to above, but we store node ids here because this list gets
   // generated before any nodes get created or re-used. Its purpose is to allow
@@ -117,13 +127,17 @@
   std::set<int> changed_node_ids;
 
   // Keeps track of new nodes created during this update.
-  std::set<AXNode*> new_nodes;
+  std::set<const AXNode*> new_nodes;
 
   // The new root in this update, if any.
   AXNode* new_root;
 
   // Keeps track of any nodes removed. Used to identify re-parented nodes.
   std::set<int> removed_node_ids;
+
+  // Maps between a node id and its data. We need to keep this around because
+  // the reparented nodes in this update were actually deleted.
+  std::map<int32_t, AXNodeData> reparented_node_id_to_data;
 };
 
 AXTree::AXTree() {
@@ -431,7 +445,7 @@
     }
   }
 
-  std::set<AXNode*>& new_nodes = update_state.new_nodes;
+  std::set<const AXNode*>& new_nodes = update_state.new_nodes;
   std::vector<AXTreeObserver::Change> changes;
   changes.reserve(update.nodes.size());
   for (size_t i = 0; i < update.nodes.size(); ++i) {
@@ -439,8 +453,8 @@
     if (!node)
       continue;
 
-    bool is_new_node = new_nodes.find(node) != new_nodes.end();
-    bool is_reparented_node = is_new_node && update_state.HasRemovedNode(node);
+    bool is_new_node = update_state.IsNewNode(node);
+    bool is_reparented_node = update_state.IsReparentedNode(node);
 
     AXTreeObserver::ChangeType change = AXTreeObserver::NODE_CHANGED;
     if (is_new_node) {
@@ -461,7 +475,7 @@
         // the root without replacing it.
         bool is_subtree = !node->parent() ||
                           new_nodes.find(node->parent()) == new_nodes.end() ||
-                          update_state.HasRemovedNode(node->parent()) ||
+                          update_state.IsRemovedNode(node->parent()) ||
                           (node->parent() == root_ && root_updated);
         change = is_subtree ? AXTreeObserver::SUBTREE_CREATED
                             : AXTreeObserver::NODE_CREATED;
@@ -531,8 +545,8 @@
   AXNode* new_node = new AXNode(this, parent, id, index_in_parent);
   id_map_[new_node->id()] = new_node;
   for (AXTreeObserver& observer : observers_) {
-    if (update_state->HasChangedNode(new_node) &&
-        !update_state->HasRemovedNode(new_node))
+    if (update_state->IsChangedNode(new_node) &&
+        !update_state->IsRemovedNode(new_node))
       observer.OnNodeCreated(this, new_node);
     else
       observer.OnNodeReparented(this, new_node);
@@ -557,8 +571,14 @@
     // TODO(accessibility): CallNodeChangeCallbacks should not pass |node|,
     // since the tree and the node data are not yet in a consistent
     // state. Possibly only pass id.
-    if (update_state->new_nodes.find(node) == update_state->new_nodes.end())
-      CallNodeChangeCallbacks(node, src);
+    if (!update_state->IsNewNode(node) ||
+        update_state->IsReparentedNode(node)) {
+      auto it = update_state->reparented_node_id_to_data.find(node->id());
+      if (it != update_state->reparented_node_id_to_data.end())
+        CallNodeChangeCallbacks(node, it->second, src);
+      else
+        CallNodeChangeCallbacks(node, node->data(), src);
+    }
     UpdateReverseRelations(node, src);
     node->SetData(src);
   } else {
@@ -621,8 +641,9 @@
   return success;
 }
 
-void AXTree::CallNodeChangeCallbacks(AXNode* node, const AXNodeData& new_data) {
-  const AXNodeData& old_data = node->data();
+void AXTree::CallNodeChangeCallbacks(AXNode* node,
+                                     const AXNodeData& old_data,
+                                     const AXNodeData& new_data) {
   for (AXTreeObserver& observer : observers_)
     observer.OnNodeDataWillChange(this, old_data, new_data);
 
@@ -783,7 +804,7 @@
                             AXTreeUpdateState* update_state) {
   DCHECK(update_state);
   for (AXTreeObserver& observer : observers_) {
-    if (!update_state->HasChangedNode(node))
+    if (!update_state->IsChangedNode(node))
       observer.OnSubtreeWillBeDeleted(this, node);
     else
       observer.OnSubtreeWillBeReparented(this, node);
@@ -806,7 +827,7 @@
   }
 
   for (AXTreeObserver& observer : observers_) {
-    if (!update_state || !update_state->HasChangedNode(node))
+    if (!update_state || !update_state->IsChangedNode(node))
       observer.OnNodeWillBeDeleted(this, node);
     else
       observer.OnNodeWillBeReparented(this, node);
@@ -818,6 +839,11 @@
     update_state->pending_nodes.erase(node);
     update_state->removed_node_ids.insert(node->id());
   }
+
+  if (update_state && update_state->IsChangedNode(node)) {
+    update_state->reparented_node_id_to_data.insert(
+        std::make_pair(node->id(), node->data()));
+  }
   node->Destroy();
 }
 
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index fa3ad15..66be06f 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -170,7 +170,9 @@
                   bool is_new_root,
                   AXTreeUpdateState* update_state);
 
-  void CallNodeChangeCallbacks(AXNode* node, const AXNodeData& new_data);
+  void CallNodeChangeCallbacks(AXNode* node,
+                               const AXNodeData& old_data,
+                               const AXNodeData& new_data);
 
   void UpdateReverseRelations(AXNode* node, const AXNodeData& new_data);
 
diff --git a/ui/aura/mus/input_method_mus.cc b/ui/aura/mus/input_method_mus.cc
index d68b1d70..589fc81 100644
--- a/ui/aura/mus/input_method_mus.cc
+++ b/ui/aura/mus/input_method_mus.cc
@@ -105,11 +105,9 @@
   if (!input_method_)
     return;
 
-  auto text_input_state = ws::mojom::TextInputState::New();
-  text_input_state->text_input_type = client->GetTextInputType();
-  text_input_state->text_input_mode = client->GetTextInputMode();
-  text_input_state->text_direction = client->GetTextDirection();
-  text_input_state->text_input_flags = client->GetTextInputFlags();
+  auto text_input_state = ws::mojom::TextInputState::New(
+      client->GetTextInputType(), client->GetTextInputMode(),
+      client->GetTextDirection(), client->GetTextInputFlags());
   input_method_->OnTextInputStateChanged(std::move(text_input_state));
 }
 
@@ -195,19 +193,15 @@
       std::make_unique<TextInputClientImpl>(focused, delegate());
 
   if (ime_driver_) {
-    ws::mojom::StartSessionDetailsPtr details =
-        ws::mojom::StartSessionDetails::New();
-    details->client =
-        text_input_client_->CreateInterfacePtrAndBind().PassInterface();
-    details->input_method_request = MakeRequest(&input_method_ptr_);
-    input_method_ = input_method_ptr_.get();
-    details->state = ws::mojom::TextInputState::New();
-    details->state->text_input_type = focused->GetTextInputType();
-    details->state->text_input_mode = focused->GetTextInputMode();
-    details->state->text_direction = focused->GetTextDirection();
-    details->state->text_input_flags = focused->GetTextInputFlags();
+    ws::mojom::SessionDetailsPtr details = ws::mojom::SessionDetails::New();
+    details->state = ws::mojom::TextInputState::New(
+        focused->GetTextInputType(), focused->GetTextInputMode(),
+        focused->GetTextDirection(), focused->GetTextInputFlags());
     details->caret_bounds = focused->GetCaretBounds();
-    ime_driver_->StartSession(std::move(details));
+    ime_driver_->StartSession(MakeRequest(&input_method_ptr_),
+                              text_input_client_->CreateInterfacePtrAndBind(),
+                              std::move(details));
+    input_method_ = input_method_ptr_.get();
   }
 }
 
diff --git a/ui/file_manager/video_player/js/BUILD.gn b/ui/file_manager/video_player/js/BUILD.gn
index 6b93e19..1090e8a 100644
--- a/ui/file_manager/video_player/js/BUILD.gn
+++ b/ui/file_manager/video_player/js/BUILD.gn
@@ -50,12 +50,16 @@
 js_library("mouse_inactivity_watcher") {
 }
 
+js_library("video_player_native_controls") {
+}
+
 js_library("video_player") {
   deps = [
     ":error_util",
     ":media_controls",
     ":mouse_inactivity_watcher",
     ":video_player_metrics",
+    ":video_player_native_controls",
     "cast:cast_video_element",
     "cast:media_manager",
     "//ui/file_manager/base/js:filtered_volume_manager",
diff --git a/ui/file_manager/video_player/js/video_player.js b/ui/file_manager/video_player/js/video_player.js
index 6edb7f8..85df394 100644
--- a/ui/file_manager/video_player/js/video_player.js
+++ b/ui/file_manager/video_player/js/video_player.js
@@ -52,14 +52,10 @@
         break;
 
       case ' ': // Space
-        if (useNativeControls) {
-          break;
-        }
       case 'k':
       case 'MediaPlayPause':
         if (!e.target.classList.contains('menu-button')) {
-          useNativeControls ? this.togglePlayState() :
-                              this.togglePlayStateWithFeedback();
+          this.togglePlayStateWithFeedback();
         }
         break;
       case 'Escape':
@@ -104,10 +100,6 @@
     this.inactivityWatcher_.kick();
   });
 
-  if (useNativeControls) {
-    return;
-  }
-
   // TODO(mtomasz): Simplify. crbug.com/254318.
   var clickInProgress = false;
   videoContainer.addEventListener('click', function(e) {
@@ -274,14 +266,6 @@
       getRequiredElement('video-container'),
       getRequiredElement('controls'));
 
-  if (useNativeControls) {
-    getRequiredElement('controls-wrapper').style.display = 'none';
-    getRequiredElement('spinner-container').style.display = 'none';
-    getRequiredElement('error-wrapper').style.display = 'none';
-    getRequiredElement('thumbnail').style.display = 'none';
-    getRequiredElement('cast-container').style.display = 'none';
-  }
-
   var observer = new MutationObserver(function(mutations) {
     var isLoadingOrDisabledChanged = mutations.some(function(mutation) {
       return mutation.attributeName === 'loading' ||
@@ -440,12 +424,6 @@
       this.videoElement_ = document.createElement('video');
       getRequiredElement('video-container').appendChild(this.videoElement_);
 
-      if (useNativeControls) {
-        this.videoElement_.controls = true;
-        this.videoElement_.controlsList = 'nodownload';
-        this.videoElement_.style.pointerEvents = 'auto';
-      }
-
       var videoUrl = video.toURL();
       var source = document.createElement('source');
       source.src = videoUrl;
@@ -873,6 +851,8 @@
 
 var player = new VideoPlayer();
 
+let nativePlayer = new NativeControlsVideoPlayer();
+
 /**
  * Initializes the load time data.
  * @param {function()} callback Called when the load time data is ready.
@@ -924,8 +904,14 @@
         util.URLsToEntries(window.appState.items, function(entries) {
           metrics.recordOpenVideoPlayerAction();
           metrics.recordNumberOfOpenedFiles(entries.length);
-          player.prepare(entries);
-          player.playFirstVideo(player, fulfill);
+
+          if (!useNativeControls) {
+            player.prepare(entries);
+            player.playFirstVideo(player, fulfill);
+          } else {
+            nativePlayer.prepare(entries);
+            nativePlayer.playFirstVideo();
+          }
         }.wrap());
       }.wrap());
     }.wrap());
diff --git a/ui/file_manager/video_player/js/video_player_native_controls.js b/ui/file_manager/video_player/js/video_player_native_controls.js
new file mode 100644
index 0000000..bb92bda
--- /dev/null
+++ b/ui/file_manager/video_player/js/video_player_native_controls.js
@@ -0,0 +1,179 @@
+// 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.
+
+/**
+ * Video player with native controls
+ *
+ * @constructor
+ * @struct
+ */
+function NativeControlsVideoPlayer() {
+  this.videos_ = null;
+  this.currentPos_ = 0;
+  this.videoElement_ = null;
+}
+
+/**
+ * Initializes the video player window. This method must be called after DOM
+ * initialization.
+ * @param {!Array<!FileEntry>} videos List of videos.
+ */
+NativeControlsVideoPlayer.prototype.prepare = function(videos) {
+  this.videos_ = videos;
+
+  // TODO: Move these setting to html and css file when
+  // we are confident to remove the feature flag
+  this.videoElement_ = document.createElement('video');
+  this.videoElement_.controls = true;
+  this.videoElement_.controlsList = 'nodownload';
+  this.videoElement_.style.pointerEvents = 'auto';
+  getRequiredElement('video-container').appendChild(this.videoElement_);
+
+  // TODO: remove the element in html when remove the feature flag
+  getRequiredElement('controls-wrapper').style.display = 'none';
+  getRequiredElement('spinner-container').style.display = 'none';
+  getRequiredElement('error-wrapper').style.display = 'none';
+  getRequiredElement('thumbnail').style.display = 'none';
+  getRequiredElement('cast-container').style.display = 'none';
+
+  this.videoElement_.addEventListener('pause', this.onPause_.bind(this));
+
+  let videoPlayerElement = getRequiredElement('video-player');
+  if (videos.length > 1) {
+    videoPlayerElement.setAttribute('multiple', true);
+  } else {
+    videoPlayerElement.removeAttribute('multiple');
+  }
+
+  let arrowRight = queryRequiredElement('.arrow-box .arrow.right');
+  arrowRight.addEventListener('click', this.advance_.wrap(this, 1));
+  let arrowLeft = queryRequiredElement('.arrow-box .arrow.left');
+  arrowLeft.addEventListener('click', this.advance_.wrap(this, 0));
+};
+
+/**
+ * Plays the first video.
+ */
+NativeControlsVideoPlayer.prototype.playFirstVideo = function() {
+  this.currentPos_ = 0;
+  this.reloadCurrentVideo_(this.onFirstVideoReady_.wrap(this));
+};
+
+/**
+ * Called when the first video is ready after starting to load.
+ * Make the video player app window the same dimension as the video source
+ * Restrict the app window inside the screen.
+ *
+ * @private
+ */
+NativeControlsVideoPlayer.prototype.onFirstVideoReady_ = function() {
+  let videoWidth = this.videoElement_.videoWidth;
+  let videoHeight = this.videoElement_.videoHeight;
+
+  let aspect = videoWidth / videoHeight;
+  let newWidth = videoWidth;
+  let newHeight = videoHeight;
+
+  let shrinkX = newWidth / window.screen.availWidth;
+  let shrinkY = newHeight / window.screen.availHeight;
+  if (shrinkX > 1 || shrinkY > 1) {
+    if (shrinkY > shrinkX) {
+      newHeight = newHeight / shrinkY;
+      newWidth = newHeight * aspect;
+    } else {
+      newWidth = newWidth / shrinkX;
+      newHeight = newWidth / aspect;
+    }
+  }
+
+  let oldLeft = window.screenX;
+  let oldTop = window.screenY;
+  let oldWidth = window.innerWidth;
+  let oldHeight = window.innerHeight;
+
+  if (!oldWidth && !oldHeight) {
+    oldLeft = window.screen.availWidth / 2;
+    oldTop = window.screen.availHeight / 2;
+  }
+
+  let appWindow = chrome.app.window.current();
+  appWindow.innerBounds.width = Math.round(newWidth);
+  appWindow.innerBounds.height = Math.round(newHeight);
+  appWindow.outerBounds.left =
+      Math.max(0, Math.round(oldLeft - (newWidth - oldWidth) / 2));
+  appWindow.outerBounds.top =
+      Math.max(0, Math.round(oldTop - (newHeight - oldHeight) / 2));
+  appWindow.show();
+
+  this.videoElement_.play();
+};
+
+/**
+ * Advance to next video when the current one ends.
+ * Not using 'ended' event because dragging the timeline
+ * thumb to the end when seeking will trigger 'ended' event.
+ *
+ * @private
+ */
+NativeControlsVideoPlayer.prototype.onPause_ = function() {
+  if (this.videoElement_.currentTime == this.videoElement_.duration) {
+    this.advance_(true);
+  }
+};
+
+/**
+ * Advances to the next (or previous) track.
+ *
+ * @param {boolean} direction True to the next, false to the previous.
+ * @private
+ */
+NativeControlsVideoPlayer.prototype.advance_ = function(direction) {
+  let newPos = this.currentPos_ + (direction ? 1 : -1);
+  if (newPos < 0 || newPos >= this.videos_.length) {
+    return;
+  }
+
+  this.currentPos_ = newPos;
+  this.reloadCurrentVideo_(function() {
+    this.videoElement_.play();
+  }.wrap(this));
+};
+
+/**
+ * Reloads the current video.
+ *
+ * @param {function()=} opt_callback Completion callback.
+ */
+NativeControlsVideoPlayer.prototype.reloadCurrentVideo_ = function(
+    opt_callback) {
+  let videoPlayerElement = getRequiredElement('video-player');
+  if (this.currentPos_ == (this.videos_.length - 1)) {
+    videoPlayerElement.setAttribute('last-video', true);
+  } else {
+    videoPlayerElement.removeAttribute('last-video');
+  }
+
+  if (this.currentPos_ === 0) {
+    videoPlayerElement.setAttribute('first-video', true);
+  } else {
+    videoPlayerElement.removeAttribute('first-video');
+  }
+
+  let currentVideo = this.videos_[this.currentPos_];
+  this.loadVideo_(currentVideo, opt_callback);
+};
+
+/**
+ * Loads the video file.
+ * @param {!FileEntry} video Entry of the video to be played.
+ * @param {function()=} opt_callback Completion callback.
+ * @private
+ */
+NativeControlsVideoPlayer.prototype.loadVideo_ = function(video, opt_callback) {
+  this.videoElement_.src = video.toURL();
+  if (opt_callback) {
+    this.videoElement_.addEventListener(
+        'loadedmetadata', opt_callback, {once: true});
+  }
+};
diff --git a/ui/file_manager/video_player/js/video_player_scripts.js b/ui/file_manager/video_player/js/video_player_scripts.js
index 61fb124d..425dbd6 100644
--- a/ui/file_manager/video_player/js/video_player_scripts.js
+++ b/ui/file_manager/video_player/js/video_player_scripts.js
@@ -54,6 +54,7 @@
 // <include src="media_controls.js">
 // <include src="mouse_inactivity_watcher.js">
 
+// <include src="video_player_native_controls.js">
 // <include src="video_player.js">
 
 window.unload = unload;
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc
index f27c9c1..6ca4d2f 100644
--- a/ui/gfx/color_space.cc
+++ b/ui/gfx/color_space.cc
@@ -10,7 +10,6 @@
 #include <sstream>
 
 #include "base/atomic_sequence_num.h"
-#include "base/containers/mru_cache.h"
 #include "base/lazy_instance.h"
 #include "base/synchronization/lock.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
@@ -25,20 +24,6 @@
 
 base::AtomicSequenceNumber g_color_space_id;
 
-// See comments in ToSkColorSpace about this cache. This cache may only be
-// accessed while holding g_sk_color_space_cache_lock.
-static const size_t kMaxCachedSkColorSpaces = 16;
-using SkColorSpaceCacheBase =
-    base::MRUCache<gfx::ColorSpace, sk_sp<SkColorSpace>>;
-class SkColorSpaceCache : public SkColorSpaceCacheBase {
- public:
-  SkColorSpaceCache() : SkColorSpaceCacheBase(kMaxCachedSkColorSpaces) {}
-};
-base::LazyInstance<SkColorSpaceCache>::Leaky g_sk_color_space_cache =
-    LAZY_INSTANCE_INITIALIZER;
-base::LazyInstance<base::Lock>::Leaky g_sk_color_space_cache_lock =
-    LAZY_INSTANCE_INITIALIZER;
-
 static bool IsAlmostZero(float value) {
   return std::abs(value) < std::numeric_limits<float>::epsilon();
 }
@@ -473,8 +458,6 @@
   }
 
   // Use the named SRGB and linear-SRGB instead of the generic constructors.
-  // These do not need to be cached because skia will always return the same
-  // pointer.
   if (primaries_ == PrimaryID::BT709) {
     if (transfer_ == TransferID::IEC61966_2_1)
       return SkColorSpace::MakeSRGB();
@@ -515,21 +498,11 @@
       break;
   }
 
-  // Maintain a gfx::ColorSpace to SkColorSpace map, so that pointer-based
-  // comparisons of SkColorSpaces will be more likely to be accurate.
-  // https://crbug.com/793116
-  base::AutoLock lock(g_sk_color_space_cache_lock.Get());
-
-  auto found = g_sk_color_space_cache.Get().Get(*this);
-  if (found != g_sk_color_space_cache.Get().end())
-    return found->second;
-
   sk_sp<SkColorSpace> sk_color_space =
       SkColorSpace::MakeRGB(transfer_fn, gamut);
   if (!sk_color_space)
     DLOG(ERROR) << "SkColorSpace::MakeRGB failed.";
 
-  g_sk_color_space_cache.Get().Put(*this, sk_color_space);
   return sk_color_space;
 }
 
diff --git a/ui/gfx/color_space_unittest.cc b/ui/gfx/color_space_unittest.cc
index 6b27741..9deba4a 100644
--- a/ui/gfx/color_space_unittest.cc
+++ b/ui/gfx/color_space_unittest.cc
@@ -133,25 +133,11 @@
   };
 
   // Test that converting from ColorSpace to SkColorSpace is producing an
-  // equivalent representation, and that the SkColorSpace ref-pointers are being
-  // globally cached.
-  sk_sp<SkColorSpace> got_sk_color_spaces[kNumTests];
-  for (size_t i = 0; i < kNumTests; ++i)
-    got_sk_color_spaces[i] = color_spaces[i].ToSkColorSpace();
+  // equivalent representation.
   for (size_t i = 0; i < kNumTests; ++i) {
-    EXPECT_TRUE(SkColorSpace::Equals(got_sk_color_spaces[i].get(),
+    EXPECT_TRUE(SkColorSpace::Equals(color_spaces[i].ToSkColorSpace().get(),
                                      sk_color_spaces[i].get()))
         << " on iteration i = " << i;
-    // ToSkColorSpace should return the same thing every time.
-    EXPECT_EQ(got_sk_color_spaces[i].get(),
-              color_spaces[i].ToSkColorSpace().get())
-        << " on iteration i = " << i;
-    // But there is no cache within Skia, except for sRGB.
-    // This test may start failing if this behavior changes.
-    if (i != 0) {
-      EXPECT_NE(got_sk_color_spaces[i].get(), sk_color_spaces[i].get())
-          << " on iteration i = " << i;
-    }
   }
 
   // Invariant test: Test that converting a SkColorSpace to a ColorSpace is
diff --git a/ui/latency/latency_tracker.cc b/ui/latency/latency_tracker.cc
index 678a2af6..88d8346 100644
--- a/ui/latency/latency_tracker.cc
+++ b/ui/latency/latency_tracker.cc
@@ -195,15 +195,6 @@
             ".TimeToScrollUpdateSwapBegin4",
         original_timestamp, gpu_swap_begin_timestamp);
 
-    // This is the same metric as above. But due to a change in rebucketing,
-    // UMA pipeline cannot process this for the chirp alerts. Hence adding a
-    // newer version the this metric above. TODO(nzolghadr): Remove it in a
-    // future milesone like M70.
-    UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS(
-        "Event.Latency." + scroll_name + "." + input_modality +
-            ".TimeToScrollUpdateSwapBegin2",
-        original_timestamp, gpu_swap_begin_timestamp);
-
     if (input_modality == "Wheel") {
       RecordUmaEventLatencyScrollWheelTimeToScrollUpdateSwapBegin2Histogram(
           original_timestamp, gpu_swap_begin_timestamp);
@@ -232,15 +223,6 @@
             ".TimeToScrollUpdateSwapBegin4",
         original_timestamp, gpu_swap_begin_timestamp);
 
-    // This is the same metric as above. But due to a change in rebucketing,
-    // UMA pipeline cannot process this for the chirp alerts. Hence adding a
-    // newer version the this metric above. TODO(nzolghadr): Remove it in a
-    // future milesone like M70.
-    UMA_HISTOGRAM_INPUT_LATENCY_HIGH_RESOLUTION_MICROSECONDS(
-        "Event.Latency." + scroll_name + "." + input_modality +
-            ".TimeToScrollUpdateSwapBegin2",
-        original_timestamp, gpu_swap_begin_timestamp);
-
     if (input_modality == "Wheel") {
       RecordUmaEventLatencyScrollWheelTimeToScrollUpdateSwapBegin2Histogram(
           original_timestamp, gpu_swap_begin_timestamp);
diff --git a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
index 7342022..3527546 100644
--- a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
+++ b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.html
@@ -4,11 +4,13 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toast/cr_toast.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="smb_browser_proxy.html">
 
@@ -27,10 +29,6 @@
         @apply --cr-form-field-label;
       }
 
-      cr-input {
-        --cr-input-error-display: none;
-      }
-
       cr-searchable-drop-down {
         display: block;
         --cr-searchable-drop-down-width: 472px;
@@ -55,27 +53,44 @@
         top: 38px;
       }
 
-      .error-message {
-        color: white;
-        font: 13px;
-        padding-bottom: 15px;
-        padding-top: 15px;
-        text-align: center;
-        white-space: normal;
-      }
-
       [slot='button-container'] {
         height: 78px;
         justify-content: space-between;
       }
+
+      #name,
+      #username {
+        --cr-input-error-display: none;
+      }
+
+      #general-error-container {
+        height: 32px;
+      }
+
+      #general-error-icon {
+       --iron-icon-fill-color: var(--google-red-600);
+      }
+
+      #general-error-message {
+        color: var(--google-red-600);
+        display: inline-block;
+        font-size: 10px;
+      }
     </style>
 
     <cr-dialog id="dialog">
       <div slot="title">[[i18n('addSmbShare')]]</div>
       <div slot="body" spellcheck="false">
+        <div id="general-error-container">
+          <div hidden="[[!shouldShowGeneralError_(currentMountError_)]]">
+            <iron-icon id="general-error-icon" icon="cr:warning"></iron-icon>
+            <div id="general-error-message">[[generalErrorText_]]</div>
+          </div>
+        </div>
         <cr-searchable-drop-down id="address" label="[[i18n('smbShareUrl')]]"
             value="{{mountUrl_}}" items="[[discoveredShares_]]"
             placeholder="\\server\share" on-change="onURLChanged_"
+            error-message-allowed
             update-value-on-input autofocus>
         </cr-searchable-drop-down>
         <cr-input id="name" label="[[i18n('smbShareName')]]"
@@ -98,22 +113,16 @@
         <div id="credentials"
             hidden="[[!shouldShowCredentialUI_(authenticationMethod_)]]">
           <cr-input id="username" label="[[i18n('smbShareUsername')]]"
-              value="{{username_}}">
+              value="{{username_}}"
+              invalid="[[shouldShowCredentialError_(currentMountError_)]]">
           </cr-input>
           <cr-input id="password" type="password"
-              label="[[i18n('smbSharePassword')]]" value="{{password_}}">
+              label="[[i18n('smbSharePassword')]]" value="{{password_}}"
+              invalid="[[shouldShowCredentialError_(currentMountError_)]]">
           </cr-input>
         </div>
       </div>
       <div slot="button-container">
-        <!-- Error toast -->
-        <div>
-          <cr-toast id="errorToast" duration="3000">
-            <div class="error-message">
-              [[addShareResultText_]]
-            </div>
-          </cr-toast>
-        </div>
         <!-- Buttons -->
         <div>
           <paper-button class="cancel-button" on-click="cancel_" id="cancel">
diff --git a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js
index bcfe42e6..7ffad1b 100644
--- a/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js
+++ b/ui/webui/resources/cr_components/chromeos/smb_shares/add_smb_share_dialog.js
@@ -6,6 +6,20 @@
  * @fileoverview 'add-smb-share-dialog' is a component for adding an SMB Share.
  */
 
+cr.define('smb_shares', function() {
+  /** @enum{number} */
+  const MountErrorType = {
+    NO_ERROR: 0,
+    CREDENTIAL_ERROR: 1,
+    PATH_ERROR: 2,
+    GENERAL_ERROR: 3,
+  };
+
+  return {
+    MountErrorType: MountErrorType,
+  };
+});
+
 Polymer({
   is: 'add-smb-share-dialog',
 
@@ -72,13 +86,19 @@
     },
 
     /** @private */
-    addShareResultText_: String,
+    generalErrorText_: String,
 
     /** @private */
     inProgress_: {
       type: Boolean,
       value: false,
-    }
+    },
+
+    /** @private {!smb_shares.MountErrorType} */
+    currentMountError_: {
+      type: Number,
+      value: smb_shares.MountErrorType.NO_ERROR,
+    },
   },
 
   /** @private {?smb_shares.SmbBrowserProxy} */
@@ -105,6 +125,7 @@
 
   /** @private */
   onAddButtonTap_: function() {
+    this.resetErrorState_();
     this.inProgress_ = true;
     this.browserProxy_
         .smbMount(
@@ -118,6 +139,7 @@
 
   /** @private */
   onURLChanged_: function() {
+    this.resetErrorState_();
     const parts = this.mountUrl_.split('\\');
     this.mountName_ = parts[parts.length - 1];
   },
@@ -152,35 +174,102 @@
    */
   onAddShare_: function(result) {
     this.inProgress_ = false;
+
+    // Success case. Close dialog.
+    if (result == SmbMountResult.SUCCESS) {
+      this.$.dialog.close();
+      return;
+    }
+
     switch (result) {
-      case SmbMountResult.SUCCESS:
-        this.$.dialog.close();
-        break;
+      // Credential Error
       case SmbMountResult.AUTHENTICATION_FAILED:
-        this.addShareResultText_ =
-            loadTimeData.getString('smbShareAddedAuthFailedMessage');
+        this.setCredentialError_(
+            loadTimeData.getString('smbShareAddedAuthFailedMessage'));
         break;
+
+      // Path Errors
       case SmbMountResult.NOT_FOUND:
-        this.addShareResultText_ =
-            loadTimeData.getString('smbShareAddedNotFoundMessage');
-        break;
-      case SmbMountResult.UNSUPPORTED_DEVICE:
-        this.addShareResultText_ =
-            loadTimeData.getString('smbShareAddedUnsupportedDeviceMessage');
-        break;
-      case SmbMountResult.MOUNT_EXISTS:
-        this.addShareResultText_ =
-            loadTimeData.getString('smbShareAddedMountExistsMessage');
+        this.setPathError_(
+            loadTimeData.getString('smbShareAddedNotFoundMessage'));
         break;
       case SmbMountResult.INVALID_URL:
-        this.addShareResultText_ =
-            loadTimeData.getString('smbShareAddedInvalidURLMessage');
+        this.setPathError_(
+            loadTimeData.getString('smbShareAddedInvalidURLMessage'));
+        break;
+
+      // General Errors
+      case SmbMountResult.UNSUPPORTED_DEVICE:
+        this.setGeneralError_(
+            loadTimeData.getString('smbShareAddedUnsupportedDeviceMessage'));
+        break;
+      case SmbMountResult.MOUNT_EXISTS:
+        this.setGeneralError_(
+            loadTimeData.getString('smbShareAddedMountExistsMessage'));
         break;
       default:
-        this.addShareResultText_ =
-            loadTimeData.getString('smbShareAddedErrorMessage');
+        this.setGeneralError_(
+            loadTimeData.getString('smbShareAddedErrorMessage'));
     }
-    this.$.errorToast.show();
   },
 
+  /** @private */
+  resetErrorState_: function() {
+    this.currentMountError_ = smb_shares.MountErrorType.NO_ERROR;
+    this.$.address.errorMessage = '';
+    this.$.password.errorMessage = '';
+    this.generalErrorText_ = '';
+  },
+
+  /**
+   * @param {string} errorMessage
+   * @private
+   */
+  setCredentialError_: function(errorMessage) {
+    this.$.password.errorMessage = errorMessage;
+    this.currentMountError_ = smb_shares.MountErrorType.CREDENTIAL_ERROR;
+  },
+
+  /**
+   * @param {string} errorMessage
+   * @private
+   */
+  setGeneralError_: function(errorMessage) {
+    this.generalErrorText_ = errorMessage;
+    this.currentMountError_ = smb_shares.MountErrorType.GENERAL_ERROR;
+  },
+
+  /**
+   * @param {string} errorMessage
+   * @private
+   */
+  setPathError_: function(errorMessage) {
+    this.$.address.errorMessage = errorMessage;
+    this.currentMountError_ = smb_shares.MountErrorType.PATH_ERROR;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowCredentialError_: function() {
+    return this.currentMountError_ ==
+        smb_shares.MountErrorType.CREDENTIAL_ERROR;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowGeneralError_: function() {
+    return this.currentMountError_ == smb_shares.MountErrorType.GENERAL_ERROR;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowPathError_: function() {
+    return this.currentMountError_ == smb_shares.MountErrorType.PATH_ERROR;
+  },
 });
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index 84c85d1..71f9af9 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -27,6 +27,7 @@
     --google-grey-refresh-300: #DADCE0;
     --google-grey-refresh-500: #9AA0A6;
     --google-grey-refresh-700: #5F6368;
+    --google-grey-refresh-900: #202124;
     --google-red-refresh-300: #f28b82;
 
     --cr-primary-text-color: var(--google-grey-900);
@@ -57,9 +58,10 @@
     --cr-primary-text-color: var(--dark-primary-color);
     --cr-secondary-text-color: var(--dark-secondary-color);
 
-    --cr-card-background-color: var(--google-grey-900);
+    --cr-card-background-color: var(--google-grey-refresh-900);
     --cr-card-elevation: {
-      background-color: rgba(255, 255, 255, .04);
+      background-image: linear-gradient(rgba(255, 255, 255, .04),
+                                        rgba(255, 255, 255, .04));
       box-shadow: rgba(0, 0, 0, .3) 0 1px 2px 0,
                   rgba(0, 0, 0, .15) 0 4px 8px 3px;
     }
diff --git a/ui/webui/resources/css/md_colors.css b/ui/webui/resources/css/md_colors.css
index 5606926..4166c6422 100644
--- a/ui/webui/resources/css/md_colors.css
+++ b/ui/webui/resources/css/md_colors.css
@@ -15,6 +15,7 @@
 html[dark],
 :host-context(html[dark]) {
   --md-background-color: rgb(32, 33, 36);  /* --google-grey-900 */
+  --md-loading-message-color: #9AA0A6;  /* --google-grey-refresh-500 */
   --md-toolbar-border-color: rgb(95, 99, 104);  /* --google-grey-refresh-700 */
   /* Dark mode doesn't currently have a different toolbar background color.
    * Instead, it uses a 1px grey border at the bottom. We set it just in case