diff --git a/DEPS b/DEPS
index 8beb64bd..e2e819e 100644
--- a/DEPS
+++ b/DEPS
@@ -133,11 +133,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': '24b0b59e4409f41d8e6749a32001a7fdbfe18e7e',
+  'skia_revision': 'b085fa9c8c19e0afd64ea77a4c018a4aef37e9ed',
   # 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': '3e8a733af17a7812eba188dad612be503bd45c57',
+  'v8_revision': '028b41c56f0450bfb0eac4325e9db629e95afd8f',
   # 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.
@@ -153,7 +153,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': '9384947f47f28811e7b15b0c267e89a277d24880',
+  'pdfium_revision': 'ed5dc24c07a33561e53522779bfc2282b4f9bcdf',
   # 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.
@@ -184,7 +184,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '6d65c60fca0ebce88e2bcfeac92a7a791e03bf42',
+  'freetype_revision': '31757f969fba60d75404f31e8f1168bef5011770',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
@@ -248,7 +248,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_headers_revision': 'dc3db3a5ae5e97e007dd2fb6b76f57bee3f809fc',
+  'spv_headers_revision': '03a081524afabdde274d885880c2fef213e46a59',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -752,7 +752,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3a9f8f45de46efae4eea2758dc6922634d121d58',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6195cd0e66d03a29c616c74d6c07d5867bd747bb',
       'condition': 'checkout_linux',
   },
 
@@ -846,7 +846,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'cc4a756d48e9c49d31265962c85fee28b5828e5f',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '79d25ea0ce184f583e07aec9989101740e139033',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1116,7 +1116,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'eb748c338927823601af8d364e1e172cd590f01c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c854c371c89b02fd1da270a80585985bb0f52c89',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1290,7 +1290,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0f51b2e123f39c9ff12e621b0b47dd28dd64424',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'd36c08623d9085dad821fbb1462a7fe03badcef5',
+    Var('webrtc_git') + '/src.git' + '@' + 'be7af9399ceb88171bf60b50419ff2dec8184fb9',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1331,7 +1331,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@327e9ae49a2d7bbfdef893e5977dd92c9dae9d1c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0563888d38680bd28f06e48686d79c936c81f4a1',
     'condition': 'checkout_src_internal',
   },
 
@@ -2749,7 +2749,7 @@
                'src/third_party/libovr/src',
     ],
   },
-  # Download ink resources for chromeos.
+  # Download common ink resources for chromeos.
   {
     'name': 'ink-build',
     'pattern': '.',
@@ -2763,7 +2763,7 @@
                 '-d', 'src/third_party/ink/build',
     ],
   },
-  # Download ink resources for chromeos.
+  # Download wasm ink resources for chromeos.
   {
     'name': 'ink-build-wasm',
     'pattern': '.',
@@ -2777,6 +2777,20 @@
                 '-d', 'src/third_party/ink/build/wasm',
     ],
   },
+  # Download wasm threaded ink resources for chromeos.
+  {
+    'name': 'ink-build-wasm',
+    'pattern': '.',
+    'condition': 'checkout_chromeos',
+    'action': [ 'python',
+                'src/third_party/depot_tools/download_from_google_storage.py',
+                '--no_resume',
+                '--no_auth',
+                '--num_threads=4',
+                '--bucket', 'chromium-ink',
+                '-d', 'src/third_party/ink/build/wasm-threads',
+    ],
+  },
   {
     # Pull doclava binaries if building for Android.
     'name': 'doclava',
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index 46e9b1e..0779796 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -8,6 +8,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.provider.MediaStore;
@@ -152,11 +153,17 @@
                 Log.w(TAG, "Unknown message level, defaulting to DEBUG");
                 break;
         }
-
+        // Calling onConsoleMessage(...) even that the result is ignored if it's a non-debuggable
+        // app or device as it can be overriden by the user with some custom logic that is expected
+        // to be always run.
         boolean result = mContentsClient.onConsoleMessage(
                 new AwConsoleMessage(message, sourceId, lineNumber, messageLevel));
-        if (result && message != null && message.startsWith("[blocked]")) {
-            Log.e(TAG, "Blocked URL: " + message);
+        boolean isAppDebuggable = mContext.getApplicationInfo().FLAG_DEBUGGABLE != 0;
+        boolean isOsDebuggable = !Build.TYPE.equals("user");
+        // Always return true if not debuggable so js console messages won't be mirrored to logcat
+        // for privacy.
+        if (!isAppDebuggable && !isOsDebuggable) {
+            return true;
         }
         return result;
     }
diff --git a/ash/perftests/ash_background_filter_blur_perftest.cc b/ash/perftests/ash_background_filter_blur_perftest.cc
index c5edf228..1bbdf3a5 100644
--- a/ash/perftests/ash_background_filter_blur_perftest.cc
+++ b/ash/perftests/ash_background_filter_blur_perftest.cc
@@ -6,7 +6,7 @@
 
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "testing/perf/perf_test.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/test/draw_waiter_for_test.h"
@@ -45,7 +45,7 @@
 
   ui::Compositor* compositor_ = nullptr;
 
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
 
   DISALLOW_COPY_AND_ASSIGN(AshBackgroundFilterBlurPerfTest);
 };
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f7791c4..f0f3461 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -892,6 +892,8 @@
     "timer/elapsed_timer.h",
     "timer/hi_res_timer_manager.h",
     "timer/hi_res_timer_manager_win.cc",
+    "timer/lap_timer.cc",
+    "timer/lap_timer.h",
     "timer/timer.cc",
     "timer/timer.h",
     "token.cc",
@@ -2576,6 +2578,7 @@
     "time/time_unittest.cc",
     "time/time_win_unittest.cc",
     "timer/hi_res_timer_manager_unittest.cc",
+    "timer/lap_timer_unittest.cc",
     "timer/mock_timer_unittest.cc",
     "timer/timer_unittest.cc",
     "token_unittest.cc",
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 0c6ea796b..87db587b 100644
--- a/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
+++ b/base/android/java/src/org/chromium/base/task/DefaultTaskExecutor.java
@@ -52,4 +52,9 @@
             runner.postDelayedTask(task, delay);
         }
     }
+
+    @Override
+    public boolean canRunTaskImmediately(TaskTraits traits) {
+        return false;
+    }
 }
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 efc15db..0b6b30d 100644
--- a/base/android/java/src/org/chromium/base/task/PostTask.java
+++ b/base/android/java/src/org/chromium/base/task/PostTask.java
@@ -87,6 +87,27 @@
     }
 
     /**
+     * This function executes the task immediately if the current thread is the
+     * same as the one corresponding to the SingleThreadTaskRunner, otherwise it
+     * posts it.
+     *
+     * It should be executed only for tasks with traits corresponding to
+     * executors backed by a SingleThreadTaskRunner, like UiThreadTaskTraits.
+     *
+     * Use this only for trivial tasks as it ignores task priorities.
+     *
+     * @param taskTraits The TaskTraits that describe the desired TaskRunner.
+     * @param task The task to be run with the specified traits.
+     */
+    public static void runOrPostTask(TaskTraits taskTraits, Runnable task) {
+        if (getTaskExecutorForTraits(taskTraits).canRunTaskImmediately(taskTraits)) {
+            task.run();
+        } else {
+            postTask(taskTraits, task);
+        }
+    }
+
+    /**
      * Registers a TaskExecutor, this must be called before any other usages of this API.
      *
      * @param extensionId The id associated with the TaskExecutor.
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 3a9e8cf..ad2b8c90 100644
--- a/base/android/java/src/org/chromium/base/task/TaskExecutor.java
+++ b/base/android/java/src/org/chromium/base/task/TaskExecutor.java
@@ -34,4 +34,10 @@
      * @return The TaskRunner for the specified TaskTraits.
      */
     public SingleThreadTaskRunner createSingleThreadTaskRunner(TaskTraits traits);
+
+    /**
+     * @return true iff the executor for these traits is backed by a SingleThreadTaskRunner
+     * associated with the current thread.
+     */
+    public boolean canRunTaskImmediately(TaskTraits traits);
 }
diff --git a/base/containers/util.h b/base/containers/util.h
index a190435..435db0d1 100644
--- a/base/containers/util.h
+++ b/base/containers/util.h
@@ -5,6 +5,8 @@
 #ifndef BASE_CONTAINERS_UTIL_H_
 #define BASE_CONTAINERS_UTIL_H_
 
+#include <stdint.h>
+
 namespace base {
 
 // TODO(crbug.com/817982): What we really need is for checked_math.h to be
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 850f177..53e28e9 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -118,7 +118,7 @@
   // contains a strong reference to this TaskQueueImpl and the
   // SequenceManagerImpl destructor calls UnregisterTaskQueue on all task
   // queues.
-  DCHECK(any_thread().unregistered)
+  DCHECK(any_thread_.unregistered)
       << "UnregisterTaskQueue must be called first!";
 #endif
 }
@@ -160,20 +160,18 @@
 
   {
     AutoLock lock(any_thread_lock_);
-    AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
-
     if (main_thread_only().time_domain)
       main_thread_only().time_domain->UnregisterQueue(this);
 
-    any_thread().unregistered = true;
+    any_thread_.unregistered = true;
 
     main_thread_only().on_task_completed_handler = OnTaskCompletedHandler();
-    any_thread().time_domain = nullptr;
+    any_thread_.time_domain = nullptr;
     main_thread_only().time_domain = nullptr;
 
     main_thread_only().on_next_wake_up_changed_callback =
         OnNextWakeUpChangedCallback();
-    immediate_incoming_queue.swap(immediate_incoming_queue_);
+    immediate_incoming_queue.swap(any_thread_.immediate_incoming_queue);
 
     empty_queues_to_reload_handle_.ReleaseAtomicFlag();
   }
@@ -221,42 +219,40 @@
   // for details.
   CHECK(task.callback);
 
-  TimeTicks now;
-  bool add_queue_time_to_tasks = sequence_manager_->GetAddQueueTimeToTasks();
-  if (delayed_fence_allowed_ || add_queue_time_to_tasks) {
-    if (current_thread == CurrentThread::kMainThread) {
-      now = main_thread_only().time_domain->Now();
-    } else {
-      AutoLock lock(any_thread_lock_);
-      now = any_thread().time_domain->Now();
-    }
-    if (add_queue_time_to_tasks) {
-      task.queue_time = now;
-    }
-  }
-
   bool should_schedule_work = false;
   {
     // TODO(alexclarke): Maybe add a main thread only immediate_incoming_queue
     // See https://crbug.com/901800
-    AutoLock lock(immediate_incoming_queue_lock_);
+    AutoLock lock(any_thread_lock_);
+    TimeTicks now;
+    bool add_queue_time_to_tasks = sequence_manager_->GetAddQueueTimeToTasks();
+    if (delayed_fence_allowed_ || add_queue_time_to_tasks) {
+      now = any_thread_.time_domain->Now();
+      if (add_queue_time_to_tasks)
+        task.queue_time = now;
+    }
+
     // The sequence number must be incremented atomically with pushing onto the
     // incoming queue. Otherwise if there are several threads posting task we
     // risk breaking the assumption that sequence numbers increase monotonically
     // within a queue.
     EnqueueOrder sequence_number = sequence_manager_->GetNextSequenceNumber();
-    bool was_immediate_incoming_queue_empty = immediate_incoming_queue_.empty();
-    immediate_incoming_queue_.push_back(
+    bool was_immediate_incoming_queue_empty =
+        any_thread_.immediate_incoming_queue.empty();
+    any_thread_.immediate_incoming_queue.push_back(
         Task(std::move(task), now, sequence_number, sequence_number));
-    sequence_manager_->WillQueueTask(&immediate_incoming_queue_.back());
+    sequence_manager_->WillQueueTask(
+        &any_thread_.immediate_incoming_queue.back());
 
     // If this queue was completely empty, then the SequenceManager needs to be
     // informed so it can reload the work queue and add us to the
     // TaskQueueSelector which can only be done from the main thread. In
     // addition it may need to schedule a DoWork if this queue isn't blocked.
-    if (was_immediate_incoming_queue_empty && immediate_work_queue_empty_) {
+    if (was_immediate_incoming_queue_empty &&
+        any_thread_.immediate_work_queue_empty) {
       empty_queues_to_reload_handle_.SetActive(true);
-      should_schedule_work = post_immediate_task_should_schedule_work_;
+      should_schedule_work =
+          any_thread_.post_immediate_task_should_schedule_work;
     }
   }
 
@@ -265,9 +261,9 @@
   // http://shortn/_ntnKNqjDQT for a discussion.
   //
   // Calling ScheduleWork outside the lock should be safe, only the main thread
-  // can mutate |post_immediate_task_should_schedule_work_|. If it transitions
-  // to false we call ScheduleWork redundantly that's harmless. If it
-  // transitions to true, the side effect of
+  // can mutate |any_thread_.post_immediate_task_should_schedule_work|. If it
+  // transitions to false we call ScheduleWork redundantly that's harmless. If
+  // it transitions to true, the side effect of
   // |empty_queues_to_reload_handle_SetActive(true)| is guaranteed to be picked
   // up by the ThreadController's call to SequenceManagerImpl::DelayTillNextTask
   // when it computes what continuation (if any) is needed.
@@ -318,7 +314,7 @@
     TimeTicks time_domain_now;
     {
       AutoLock lock(any_thread_lock_);
-      time_domain_now = any_thread().time_domain->Now();
+      time_domain_now = any_thread_.time_domain->Now();
     }
     TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
     if (sequence_manager_->GetAddQueueTimeToTasks()) {
@@ -389,13 +385,13 @@
 }
 
 void TaskQueueImpl::TakeImmediateIncomingQueueTasks(TaskDeque* queue) {
-  AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
+  AutoLock any_thread_lock(any_thread_lock_);
   DCHECK(queue->empty());
-  queue->swap(immediate_incoming_queue_);
+  queue->swap(any_thread_.immediate_incoming_queue);
 
   // Since |immediate_incoming_queue| is empty, now is a good time to consider
   // reducing it's capacity if we're wasting memory.
-  immediate_incoming_queue_.MaybeShrinkQueue();
+  any_thread_.immediate_incoming_queue.MaybeShrinkQueue();
 
   // Activate delayed fence if necessary. This is ideologically similar to
   // ActivateDelayedFenceIfNeeded, but due to immediate tasks being posted
@@ -429,8 +425,8 @@
     return false;
   }
 
-  AutoLock lock(immediate_incoming_queue_lock_);
-  return immediate_incoming_queue_.empty();
+  AutoLock lock(any_thread_lock_);
+  return any_thread_.immediate_incoming_queue.empty();
 }
 
 size_t TaskQueueImpl::GetNumberOfPendingTasks() const {
@@ -439,8 +435,8 @@
   task_count += main_thread_only().delayed_incoming_queue.size();
   task_count += main_thread_only().immediate_work_queue->Size();
 
-  AutoLock lock(immediate_incoming_queue_lock_);
-  task_count += immediate_incoming_queue_.size();
+  AutoLock lock(any_thread_lock_);
+  task_count += any_thread_.immediate_incoming_queue.size();
   return task_count;
 }
 
@@ -460,8 +456,8 @@
   }
 
   // Finally tasks on |immediate_incoming_queue| count as immediate work.
-  AutoLock lock(immediate_incoming_queue_lock_);
-  return !immediate_incoming_queue_.empty();
+  AutoLock lock(any_thread_lock_);
+  return !any_thread_.immediate_incoming_queue.empty();
 }
 
 Optional<DelayedWakeUp> TaskQueueImpl::GetNextScheduledWakeUpImpl() {
@@ -520,9 +516,9 @@
   if (!associated_thread_->IsBoundToCurrentThread())
     return;
 
-  AutoLock lock(immediate_incoming_queue_lock_);
+  AutoLock lock(any_thread_lock_);
   TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("sequence_manager"), GetName(),
-                 immediate_incoming_queue_.size() +
+                 any_thread_.immediate_incoming_queue.size() +
                      main_thread_only().immediate_work_queue->Size() +
                      main_thread_only().delayed_work_queue->Size() +
                      main_thread_only().delayed_incoming_queue.size());
@@ -545,10 +541,9 @@
                                 trace_event::TracedValue* state,
                                 bool force_verbose) const {
   AutoLock lock(any_thread_lock_);
-  AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
   state->BeginDictionary();
   state->SetString("name", GetName());
-  if (any_thread().unregistered) {
+  if (any_thread_.unregistered) {
     state->SetBoolean("unregistered", true);
     state->EndDictionary();
     return;
@@ -564,8 +559,8 @@
   state->SetBoolean("enabled", IsQueueEnabled());
   state->SetString("time_domain_name",
                    main_thread_only().time_domain->GetName());
-  state->SetInteger("immediate_incoming_queue_size",
-                    immediate_incoming_queue_.size());
+  state->SetInteger("any_thread_.immediate_incoming_queuesize",
+                    any_thread_.immediate_incoming_queue.size());
   state->SetInteger("delayed_incoming_queue_size",
                     main_thread_only().delayed_incoming_queue.size());
   state->SetInteger("immediate_work_queue_size",
@@ -573,8 +568,8 @@
   state->SetInteger("delayed_work_queue_size",
                     main_thread_only().delayed_work_queue->Size());
 
-  state->SetInteger("immediate_incoming_queue_capacity",
-                    immediate_incoming_queue_.capacity());
+  state->SetInteger("any_thread_.immediate_incoming_queuecapacity",
+                    any_thread_.immediate_incoming_queue.capacity());
   state->SetInteger("immediate_work_queue_capacity",
                     immediate_work_queue()->Capacity());
   state->SetInteger("delayed_work_queue_capacity",
@@ -602,7 +597,7 @@
 
   if (verbose || force_verbose) {
     state->BeginArray("immediate_incoming_queue");
-    QueueAsValueInto(immediate_incoming_queue_, now, state);
+    QueueAsValueInto(any_thread_.immediate_incoming_queue, now, state);
     state->EndArray();
     state->BeginArray("delayed_work_queue");
     main_thread_only().delayed_work_queue->AsValueInto(now, state);
@@ -647,14 +642,14 @@
   {
     AutoLock lock(any_thread_lock_);
     DCHECK(time_domain);
-    DCHECK(!any_thread().unregistered);
-    if (any_thread().unregistered)
+    DCHECK(!any_thread_.unregistered);
+    if (any_thread_.unregistered)
       return;
     DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
     if (time_domain == main_thread_only().time_domain)
       return;
 
-    any_thread().time_domain = time_domain;
+    any_thread_.time_domain = time_domain;
   }
 
   main_thread_only().time_domain->UnregisterQueue(this);
@@ -697,11 +692,13 @@
       main_thread_only().delayed_work_queue->InsertFence(current_fence);
 
   {
-    AutoLock lock(immediate_incoming_queue_lock_);
+    AutoLock lock(any_thread_lock_);
     if (!task_unblocked && previous_fence && previous_fence < current_fence) {
-      if (!immediate_incoming_queue_.empty() &&
-          immediate_incoming_queue_.front().enqueue_order() > previous_fence &&
-          immediate_incoming_queue_.front().enqueue_order() < current_fence) {
+      if (!any_thread_.immediate_incoming_queue.empty() &&
+          any_thread_.immediate_incoming_queue.front().enqueue_order() >
+              previous_fence &&
+          any_thread_.immediate_incoming_queue.front().enqueue_order() <
+              current_fence) {
         task_unblocked = true;
       }
     }
@@ -732,10 +729,11 @@
   task_unblocked |= main_thread_only().delayed_work_queue->RemoveFence();
 
   {
-    AutoLock lock(immediate_incoming_queue_lock_);
+    AutoLock lock(any_thread_lock_);
     if (!task_unblocked && previous_fence) {
-      if (!immediate_incoming_queue_.empty() &&
-          immediate_incoming_queue_.front().enqueue_order() > previous_fence) {
+      if (!any_thread_.immediate_incoming_queue.empty() &&
+          any_thread_.immediate_incoming_queue.front().enqueue_order() >
+              previous_fence) {
         task_unblocked = true;
       }
     }
@@ -756,11 +754,11 @@
     return false;
   }
 
-  AutoLock lock(immediate_incoming_queue_lock_);
-  if (immediate_incoming_queue_.empty())
+  AutoLock lock(any_thread_lock_);
+  if (any_thread_.immediate_incoming_queue.empty())
     return true;
 
-  return immediate_incoming_queue_.front().enqueue_order() >
+  return any_thread_.immediate_incoming_queue.front().enqueue_order() >
          main_thread_only().current_fence;
 }
 
@@ -833,7 +831,7 @@
   bool has_pending_immediate_work;
 
   {
-    AutoLock lock(immediate_incoming_queue_lock_);
+    AutoLock lock(any_thread_lock_);
     UpdateCrossThreadQueueStateLocked();
     has_pending_immediate_work = HasPendingImmediateWorkLocked();
   }
@@ -854,18 +852,18 @@
 }
 
 void TaskQueueImpl::UpdateCrossThreadQueueStateLocked() {
-  immediate_work_queue_empty_ =
+  any_thread_.immediate_work_queue_empty =
       main_thread_only().immediate_work_queue->Empty();
 
   if (main_thread_only().on_next_wake_up_changed_callback) {
     // If there's a callback we need a DoWork for the callback to be issued by
     // ReloadEmptyImmediateWorkQueue. The callback isn't
     // sent for disabled queues.
-    post_immediate_task_should_schedule_work_ = IsQueueEnabled();
+    any_thread_.post_immediate_task_should_schedule_work = IsQueueEnabled();
   } else {
     // Otherwise we need PostImmediateTaskImpl to ScheduleWork unless the queue
     // is blocked or disabled.
-    post_immediate_task_should_schedule_work_ =
+    any_thread_.post_immediate_task_should_schedule_work =
         IsQueueEnabled() && !main_thread_only().current_fence;
   }
 }
@@ -880,8 +878,8 @@
   main_thread_only().immediate_work_queue->MaybeShrinkQueue();
 
   {
-    AutoLock lock(immediate_incoming_queue_lock_);
-    immediate_incoming_queue_.MaybeShrinkQueue();
+    AutoLock lock(any_thread_lock_);
+    any_thread_.immediate_incoming_queue.MaybeShrinkQueue();
   }
 
   LazyNow lazy_now(now);
@@ -889,8 +887,8 @@
 }
 
 void TaskQueueImpl::PushImmediateIncomingTaskForTest(Task&& task) {
-  AutoLock lock(immediate_incoming_queue_lock_);
-  immediate_incoming_queue_.push_back(std::move(task));
+  AutoLock lock(any_thread_lock_);
+  any_thread_.immediate_incoming_queue.push_back(std::move(task));
 }
 
 void TaskQueueImpl::RequeueDeferredNonNestableTask(
@@ -906,15 +904,16 @@
   } else {
     // We're about to push |task| onto an empty |immediate_work_queue|. This
     // may mean we'd be violating the contract of AtomicFlagSet, it's
-    // only supposed to contain queues where |immediate_incoming_queue_| is
-    // non empty but |immediate_work_queue| is empty. We remedy that by removing
-    // ourselves from that list (a NOP if we're not in the list).
+    // only supposed to contain queues where
+    // |any_thread_.immediate_incoming_queue| is non empty but
+    // |immediate_work_queue| is empty. We remedy that by removing ourselves
+    // from that list (a NOP if we're not in the list).
     if (main_thread_only().immediate_work_queue->Empty()) {
       empty_queues_to_reload_handle_.SetActive(false);
 
       {
-        AutoLock lock(immediate_incoming_queue_lock_);
-        immediate_work_queue_empty_ = false;
+        AutoLock lock(any_thread_lock_);
+        any_thread_.immediate_work_queue_empty = false;
         main_thread_only().immediate_work_queue->PushNonNestableTaskToFront(
             std::move(task.task));
       }
@@ -974,15 +973,14 @@
   }
 
   // Finally tasks on |immediate_incoming_queue| count as immediate work.
-  AutoLock lock(immediate_incoming_queue_lock_);
-  return !immediate_incoming_queue_.empty();
+  AutoLock lock(any_thread_lock_);
+  return !any_thread_.immediate_incoming_queue.empty();
 }
 
 bool TaskQueueImpl::HasPendingImmediateWorkLocked() {
-  immediate_incoming_queue_lock_.AssertAcquired();
   return !main_thread_only().delayed_work_queue->Empty() ||
          !main_thread_only().immediate_work_queue->Empty() ||
-         !immediate_incoming_queue_.empty();
+         !any_thread_.immediate_incoming_queue.empty();
 }
 
 void TaskQueueImpl::SetOnTaskStartedHandler(
@@ -1016,7 +1014,7 @@
 
 bool TaskQueueImpl::IsUnregistered() const {
   AutoLock lock(any_thread_lock_);
-  return any_thread().unregistered;
+  return any_thread_.unregistered;
 }
 
 WeakPtr<SequenceManagerImpl> TaskQueueImpl::GetSequenceManagerWeakPtr() {
@@ -1044,9 +1042,9 @@
   {
     // Limit the scope of the lock to ensure that the deque is destroyed
     // outside of the lock to allow it to post tasks.
-    base::AutoLock lock(immediate_incoming_queue_lock_);
-    deque.swap(immediate_incoming_queue_);
-    immediate_work_queue_empty_ = true;
+    base::AutoLock lock(any_thread_lock_);
+    deque.swap(any_thread_.immediate_incoming_queue);
+    any_thread_.immediate_work_queue_empty = true;
   }
 
   LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
@@ -1061,8 +1059,8 @@
   if (!main_thread_only().delayed_incoming_queue.empty())
     return true;
 
-  base::AutoLock lock(immediate_incoming_queue_lock_);
-  if (!immediate_incoming_queue_.empty())
+  base::AutoLock lock(any_thread_lock_);
+  if (!any_thread_.immediate_incoming_queue.empty())
     return true;
 
   return false;
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 24af883..f06f599 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -146,7 +146,7 @@
   // Used to check if we need to generate notifications about delayed work.
   bool HasPendingImmediateWork();
   bool HasPendingImmediateWorkLocked()
-      EXCLUSIVE_LOCKS_REQUIRED(immediate_incoming_queue_lock_);
+      EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
 
   bool has_pending_high_resolution_tasks() const {
     return main_thread_only()
@@ -282,18 +282,6 @@
     const int task_type_;
   };
 
-  struct AnyThread {
-    explicit AnyThread(TimeDomain* time_domain);
-    ~AnyThread();
-
-    // TimeDomain is maintained in two copies: inside AnyThread and inside
-    // MainThreadOnly. It can be changed only from main thread, so it should be
-    // locked before accessing from other threads.
-    TimeDomain* time_domain;
-
-    bool unregistered = false;
-  };
-
   // A queue for holding delayed tasks before their delay has expired.
   struct DelayedIncomingQueue {
    public:
@@ -376,7 +364,8 @@
 
   void ScheduleDelayedWorkTask(Task pending_task);
 
-  void MoveReadyImmediateTasksToImmediateWorkQueueLocked();
+  void MoveReadyImmediateTasksToImmediateWorkQueueLocked()
+      EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
 
   // LazilyDeallocatedDeque use TimeTicks to figure out when to resize.  We
   // should use real time here always.
@@ -409,9 +398,9 @@
   // Activate a delayed fence if a time has come.
   void ActivateDelayedFenceIfNeeded(TimeTicks now);
 
-  // Updates state protected by immediate_incoming_queue_lock_.
+  // Updates state protected by any_thread_lock_.
   void UpdateCrossThreadQueueStateLocked()
-      EXCLUSIVE_LOCKS_REQUIRED(immediate_incoming_queue_lock_);
+      EXCLUSIVE_LOCKS_REQUIRED(any_thread_lock_);
 
   const char* name_;
   SequenceManagerImpl* const sequence_manager_;
@@ -421,15 +410,27 @@
   const scoped_refptr<GuardedTaskPoster> task_poster_;
 
   mutable Lock any_thread_lock_;
-  AnyThread any_thread_;
-  struct AnyThread& any_thread() {
-    any_thread_lock_.AssertAcquired();
-    return any_thread_;
-  }
-  const struct AnyThread& any_thread() const {
-    any_thread_lock_.AssertAcquired();
-    return any_thread_;
-  }
+
+  struct AnyThread {
+    explicit AnyThread(TimeDomain* time_domain);
+    ~AnyThread();
+
+    // TimeDomain is maintained in two copies: inside AnyThread and inside
+    // MainThreadOnly. It can be changed only from main thread, so it should be
+    // locked before accessing from other threads.
+    TimeDomain* time_domain;
+
+    TaskDeque immediate_incoming_queue;
+
+    // True if main_thread_only().immediate_work_queue is empty.
+    bool immediate_work_queue_empty = true;
+
+    bool post_immediate_task_should_schedule_work = true;
+
+    bool unregistered = false;
+  };
+
+  AnyThread any_thread_ GUARDED_BY(any_thread_lock_);
 
   MainThreadOnly main_thread_only_;
   MainThreadOnly& main_thread_only() {
@@ -441,15 +442,6 @@
     return main_thread_only_;
   }
 
-  mutable Lock immediate_incoming_queue_lock_;
-  TaskDeque immediate_incoming_queue_
-      GUARDED_BY(immediate_incoming_queue_lock_);
-  // True if main_thread_only().immediate_work_queue is empty.
-  bool immediate_work_queue_empty_ GUARDED_BY(immediate_incoming_queue_lock_) =
-      true;
-  bool post_immediate_task_should_schedule_work_
-      GUARDED_BY(immediate_incoming_queue_lock_) = true;
-
   // Handle to our entry within the SequenceManagers |empty_queues_to_reload_|
   // atomic flag set. Used to signal that this queue needs to be reloaded.
   AtomicFlagSet::AtomicFlag empty_queues_to_reload_handle_;
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index d601723..b9827df 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -191,9 +191,12 @@
     // We don't need to call ReclaimMemory here because
     // DelayTillNextTask will have dealt with cancelled delayed tasks for us.
     Optional<TimeTicks> run_time = NextScheduledRunTime();
-    if (!run_time) {
-      // We've run out of tasks, but ScopedTaskEnvironment::FastForwardBy
-      // requires the virtual time to be consumed.
+    if (!run_time || run_time == now_ticks_) {
+      // We've run out of tasks (or an immediate task came in racily from
+      // another thread after reaching idle, ignore it, it will be processed in
+      // the next run as-if it arrived slightly later).
+      // ScopedTaskEnvironment::FastForwardBy requires the remaining virtual
+      // time to be consumed upon reaching idle.
       if (now_ticks_ < allow_advance_until_ && !allow_advance_until_.is_max())
         SetTime(allow_advance_until_);
       return false;
@@ -432,7 +435,6 @@
 }
 
 sequence_manager::TimeDomain* ScopedTaskEnvironment::GetTimeDomain() const {
-  DCHECK(subclass_creates_default_taskrunner_);
   return mock_time_domain_ ? mock_time_domain_.get()
                            : sequence_manager_->GetRealTimeDomain();
 }
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index 1c6fa67..f79a971281 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/synchronization/atomic_flag.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/post_task.h"
+#include "base/task/sequence_manager/time_domain.h"
 #include "base/test/mock_callback.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
@@ -38,6 +39,19 @@
 
 namespace {
 
+class ScopedTaskEnvironmentForTest : public ScopedTaskEnvironment {
+ public:
+  template <
+      class... ArgTypes,
+      class CheckArgumentsAreValid = std::enable_if_t<
+          base::trait_helpers::AreValidTraits<ScopedTaskEnvironment::ValidTrait,
+                                              ArgTypes...>::value>>
+  NOINLINE ScopedTaskEnvironmentForTest(const ArgTypes... args)
+      : ScopedTaskEnvironment(args...) {}
+
+  using ScopedTaskEnvironment::GetTimeDomain;
+};
+
 class ScopedTaskEnvironmentTest
     : public testing::TestWithParam<ScopedTaskEnvironment::MainThreadType> {};
 
@@ -345,6 +359,46 @@
   EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
 }
 
+TEST_F(ScopedTaskEnvironmentTest, MockTimeDomain_MaybeFastForwardToNextTask) {
+  constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
+  ScopedTaskEnvironmentForTest scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
+  const TimeTicks start_time = base::TimeTicks::Now();
+  EXPECT_FALSE(
+      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
+          false));
+  EXPECT_EQ(start_time, base::TimeTicks::Now());
+
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
+                                                 kDelay);
+  EXPECT_TRUE(
+      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
+          false));
+  EXPECT_EQ(start_time + kDelay, base::TimeTicks::Now());
+}
+
+TEST_F(ScopedTaskEnvironmentTest,
+       MockTimeDomain_MaybeFastForwardToNextTask_ImmediateTaskPending) {
+  constexpr base::TimeDelta kDelay = TimeDelta::FromSeconds(42);
+  ScopedTaskEnvironmentForTest scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
+  const TimeTicks start_time = base::TimeTicks::Now();
+  EXPECT_FALSE(
+      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
+          false));
+  EXPECT_EQ(start_time, base::TimeTicks::Now());
+
+  ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
+                                                 kDelay);
+  ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::DoNothing());
+  EXPECT_FALSE(
+      scoped_task_environment.GetTimeDomain()->MaybeFastForwardToNextTask(
+          false));
+  EXPECT_EQ(start_time, base::TimeTicks::Now());
+}
+
 #if defined(OS_WIN)
 // Regression test to ensure that ScopedTaskEnvironment enables the MTA in the
 // thread pool (so that the test environment matches that of the browser process
diff --git a/base/timer/lap_timer.cc b/base/timer/lap_timer.cc
new file mode 100644
index 0000000..3ff2465
--- /dev/null
+++ b/base/timer/lap_timer.cc
@@ -0,0 +1,118 @@
+// 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 "base/timer/lap_timer.h"
+#include "base/logging.h"
+
+namespace base {
+
+namespace {
+
+// Default values.
+constexpr TimeDelta kDefaultTimeLimit = TimeDelta::FromSeconds(3);
+constexpr int kDefaultWarmupRuns = 5;
+constexpr int kDefaultTimeCheckInterval = 10;
+
+}  // namespace
+
+LapTimer::LapTimer(int warmup_laps,
+                   TimeDelta time_limit,
+                   int check_interval,
+                   LapTimer::TimerMethod method)
+    : warmup_laps_(warmup_laps),
+      time_limit_(time_limit),
+      check_interval_(check_interval),
+      method_(method) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+  DCHECK_GT(check_interval, 0);
+  Reset();
+}
+
+LapTimer::LapTimer(LapTimer::TimerMethod method)
+    : LapTimer(kDefaultWarmupRuns,
+               kDefaultTimeLimit,
+               kDefaultTimeCheckInterval,
+               method) {}
+
+void LapTimer::Reset() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (ThreadTicks::IsSupported() && method_ == TimerMethod::kUseThreadTicks)
+    ThreadTicks::WaitUntilInitialized();
+  num_laps_ = 0;
+  remaining_warmups_ = warmup_laps_;
+  remaining_no_check_laps_ = check_interval_;
+  Start();
+}
+
+void LapTimer::Start() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(num_laps_, 0);
+  // last_timed_ variables are initialized here (instead of in the constructor)
+  // because not all platforms support ThreadTicks.
+  if (method_ == TimerMethod::kUseThreadTicks) {
+    start_thread_ticks_ = ThreadTicks::Now();
+    last_timed_lap_end_thread_ticks_ = ThreadTicks::Now();
+  } else {
+    start_time_ticks_ = TimeTicks::Now();
+    last_timed_lap_end_ticks_ = TimeTicks::Now();
+  }
+}
+
+bool LapTimer::IsWarmedUp() const {
+  return remaining_warmups_ <= 0;
+}
+
+void LapTimer::NextLap() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!start_thread_ticks_.is_null() || !start_time_ticks_.is_null());
+  if (!IsWarmedUp()) {
+    --remaining_warmups_;
+    if (IsWarmedUp()) {
+      Start();
+    }
+    return;
+  }
+  ++num_laps_;
+  --remaining_no_check_laps_;
+  if (!remaining_no_check_laps_) {
+    if (method_ == TimerMethod::kUseTimeTicks) {
+      last_timed_lap_end_ticks_ = TimeTicks::Now();
+    } else {
+      last_timed_lap_end_thread_ticks_ = ThreadTicks::Now();
+    }
+    remaining_no_check_laps_ = check_interval_;
+  }
+}
+
+TimeDelta LapTimer::GetAccumulatedTime() const {
+  if (method_ == TimerMethod::kUseTimeTicks) {
+    return last_timed_lap_end_ticks_ - start_time_ticks_;
+  }
+  return last_timed_lap_end_thread_ticks_ - start_thread_ticks_;
+}
+
+bool LapTimer::HasTimeLimitExpired() const {
+  return GetAccumulatedTime() >= time_limit_;
+}
+
+bool LapTimer::HasTimedAllLaps() const {
+  return num_laps_ && !(num_laps_ % check_interval_);
+}
+
+TimeDelta LapTimer::TimePerLap() const {
+  DCHECK(HasTimedAllLaps());
+  DCHECK_GT(num_laps_, 0);
+  return GetAccumulatedTime() / num_laps_;
+}
+
+float LapTimer::LapsPerSecond() const {
+  DCHECK(HasTimedAllLaps());
+  DCHECK_GT(num_laps_, 0);
+  return num_laps_ / GetAccumulatedTime().InSecondsF();
+}
+
+int LapTimer::NumLaps() const {
+  return num_laps_;
+}
+}  // namespace base
diff --git a/base/timer/lap_timer.h b/base/timer/lap_timer.h
new file mode 100644
index 0000000..c28a0df
--- /dev/null
+++ b/base/timer/lap_timer.h
@@ -0,0 +1,87 @@
+// 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 BASE_TIMER_LAP_TIMER_H_
+#define BASE_TIMER_LAP_TIMER_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// LapTimer is used to calculate average times per "Lap" in perf tests.
+// NextLap increments the lap counter, used in counting the per lap averages.
+// If you initialize the LapTimer with a non zero |warmup_laps|, it will ignore
+// the times for that many laps at the start.
+// If you set the |time_limit| then you can use HasTimeLimitExpired() to see if
+// the current accumulated time has crossed that threshold, with an optimization
+// that it only tests this every |check_interval| laps.
+//
+// See base/timer/lap_timer_unittest.cc for a usage example.
+//
+class BASE_EXPORT LapTimer {
+ public:
+  enum class TimerMethod {
+    // Measures CPU time consumed by the thread running the LapTimer.
+    kUseThreadTicks,
+    // Measures elapsed wall time (default).
+    kUseTimeTicks
+  };
+
+  LapTimer(int warmup_laps,
+           TimeDelta time_limit,
+           int check_interval,
+           TimerMethod timing_method = TimerMethod::kUseTimeTicks);
+  // Create LapTimer with sensible default values.
+  LapTimer(TimerMethod timing_method = TimerMethod::kUseTimeTicks);
+  // Sets the timer back to its starting state.
+  void Reset();
+  // Sets the start point to now.
+  void Start();
+  // Returns true if there are no more warmup laps to do.
+  bool IsWarmedUp() const;
+  // Advance the lap counter and update the accumulated time.
+  // The accumulated time is only updated every check_interval laps.
+  // If accumulating then the start point will also be updated.
+  void NextLap();
+  // Returns true if the stored time has exceeded the time limit specified.
+  // May cause a call to Store().
+  bool HasTimeLimitExpired() const;
+  // The average time taken per lap.
+  TimeDelta TimePerLap() const;
+  // The number of laps per second.
+  float LapsPerSecond() const;
+  // The number of laps recorded.
+  int NumLaps() const;
+
+ private:
+  // Returns true if all lap times have been timed. Only true every n'th
+  // lap, where n = check_interval.
+  bool HasTimedAllLaps() const;
+  // Returns the current accumulated time.
+  TimeDelta GetAccumulatedTime() const;
+
+  const int warmup_laps_;
+  const TimeDelta time_limit_;
+  const int check_interval_;
+  const TimerMethod method_;
+
+  ThreadTicks start_thread_ticks_;
+  TimeTicks start_time_ticks_;
+
+  ThreadTicks last_timed_lap_end_thread_ticks_;
+  TimeTicks last_timed_lap_end_ticks_;
+
+  int num_laps_;
+  int remaining_warmups_ = 0;
+  int remaining_no_check_laps_ = 0;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  DISALLOW_COPY_AND_ASSIGN(LapTimer);
+};
+}  // namespace base
+
+#endif  // BASE_TIMER_LAP_TIMER_H_
diff --git a/base/timer/lap_timer_unittest.cc b/base/timer/lap_timer_unittest.cc
new file mode 100644
index 0000000..b45ab39
--- /dev/null
+++ b/base/timer/lap_timer_unittest.cc
@@ -0,0 +1,85 @@
+// 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/timer/lap_timer.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This file contains a minimal unit test for LapTimer, used for benchmarking.
+// This file is supposed to match closely with the example code, documented in
+// lap_timer.h. Please update that documentation if you need to change things.
+
+namespace base {
+
+namespace test {
+
+namespace {
+
+constexpr base::TimeDelta kTimeLimit = base::TimeDelta::FromMilliseconds(15);
+constexpr base::TimeDelta kTimeAdvance = base::TimeDelta::FromMilliseconds(1);
+constexpr int kWarmupRuns = 5;
+constexpr int kTimeCheckInterval = 10;
+
+}  // namespace
+
+TEST(LapTimer, UsageExample) {
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
+
+  // Advance time a little bit so that TimeTicks::Now().is_null() becomes false.
+  scoped_task_environment.FastForwardBy(kTimeAdvance);
+
+  LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
+
+  EXPECT_FALSE(timer.HasTimeLimitExpired());
+  EXPECT_FALSE(timer.IsWarmedUp());
+
+  do {
+    scoped_task_environment.FastForwardBy(kTimeAdvance);
+    timer.NextLap();
+  } while (!timer.HasTimeLimitExpired());
+
+  EXPECT_NEAR(timer.LapsPerSecond(), 1000, 0.1);
+  EXPECT_NEAR(timer.TimePerLap().InMillisecondsF(), 1.0f, 0.1);
+  // Output number of laps is 20, because the warm up runs are ignored and the
+  // timer is only checked every kTimeInterval laps.
+  EXPECT_EQ(timer.NumLaps(), 20);
+
+  EXPECT_TRUE(timer.HasTimeLimitExpired());
+  EXPECT_TRUE(timer.IsWarmedUp());
+}
+
+#if !defined(OS_IOS)
+// iOS simulator does not support using ThreadTicks.
+TEST(LapTimer, ThreadTicksUsageExample) {
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+      ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME);
+  LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval,
+                 LapTimer::TimerMethod::kUseThreadTicks);
+
+  EXPECT_FALSE(timer.HasTimeLimitExpired());
+  EXPECT_FALSE(timer.IsWarmedUp());
+
+  do {
+    scoped_task_environment.FastForwardBy(kTimeAdvance);
+    timer.NextLap();
+  } while (!timer.HasTimeLimitExpired());
+
+  // Because advancing the ScopedTaskEnvironment time won't affect the
+  // ThreadTicks, laps will be much faster than the regular UsageExample.
+  EXPECT_GT(timer.LapsPerSecond(), 1000);
+  EXPECT_LT(timer.TimePerLap().InMillisecondsF(), 1.0f);
+  EXPECT_GT(timer.NumLaps(), 20);
+
+  EXPECT_TRUE(timer.HasTimeLimitExpired());
+  EXPECT_TRUE(timer.IsWarmedUp());
+}
+#endif
+
+}  // namespace test
+}  // namespace base
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 135cdf3..38329cb 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-882938bbc85eb6781490026cad45e29448c26cf1
\ No newline at end of file
+8384d21f4cd044e49e9688d570fa8527acfc709e
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index d45cd81..6fd5deb 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-1a7a29783cf061f4b571e4ebc8f56a7dc1a82935
\ No newline at end of file
+81f6ea6d6e06600e407138c46ad4439b1db09a5e
\ No newline at end of file
diff --git a/cc/animation/animation_host_perftest.cc b/cc/animation/animation_host_perftest.cc
index afadb46..3bd2568 100644
--- a/cc/animation/animation_host_perftest.cc
+++ b/cc/animation/animation_host_perftest.cc
@@ -5,11 +5,11 @@
 #include "cc/animation/animation_host.h"
 
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/timer/lap_timer.h"
 #include "cc/animation/animation_id_provider.h"
 #include "cc/animation/animation_timeline.h"
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/single_keyframe_effect_animation.h"
-#include "cc/base/lap_timer.h"
 #include "cc/test/fake_impl_task_runner_provider.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -149,7 +149,7 @@
   int first_animation_id_;
   int last_animation_id_;
 
-  LapTimer timer_;
+  base::LapTimer timer_;
   TestTaskGraphRunner task_graph_runner_;
 };
 
diff --git a/cc/base/BUILD.gn b/cc/base/BUILD.gn
index 8f8a1845..9546cd27 100644
--- a/cc/base/BUILD.gn
+++ b/cc/base/BUILD.gn
@@ -20,8 +20,6 @@
     "index_rect.h",
     "invalidation_region.cc",
     "invalidation_region.h",
-    "lap_timer.cc",
-    "lap_timer.h",
     "list_container.h",
     "list_container_helper.cc",
     "list_container_helper.h",
diff --git a/cc/base/DEPS b/cc/base/DEPS
index 1bcb1a1..909b4a27 100644
--- a/cc/base/DEPS
+++ b/cc/base/DEPS
@@ -9,8 +9,4 @@
   ".*unittest\.cc": [
     "+cc/test",
   ],
-  # Allow lap_timer.h for perftests
-  ".*perftest\.cc": [
-    "+cc/debug/lap_timer.h",
-  ],
 }
diff --git a/cc/base/lap_timer.cc b/cc/base/lap_timer.cc
deleted file mode 100644
index 42335a8c..0000000
--- a/cc/base/lap_timer.cc
+++ /dev/null
@@ -1,105 +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 "cc/base/lap_timer.h"
-
-#include "base/logging.h"
-
-namespace cc {
-
-namespace {
-
-// Returns the offset from the origin from the ThreadTicks time source.
-// TimeTicks is used as a fallback if ThreadTicks is not available on the
-// current platform.
-base::TimeDelta Now() {
-  return base::ThreadTicks::IsSupported()
-             ? base::ThreadTicks::Now().since_origin()
-             : base::TimeTicks::Now().since_origin();
-}
-
-// Default values.
-static const int kTimeLimitMillis = 3000;
-static const int kWarmupRuns = 5;
-static const int kTimeCheckInterval = 10;
-
-}  // namespace
-
-LapTimer::LapTimer(int warmup_laps,
-                   base::TimeDelta time_limit,
-                   int check_interval)
-    : warmup_laps_(warmup_laps),
-      remaining_warmups_(0),
-      remaining_no_check_laps_(0),
-      time_limit_(time_limit),
-      check_interval_(check_interval) {
-  DCHECK_GT(check_interval, 0);
-  Reset();
-}
-
-LapTimer::LapTimer()
-    : LapTimer(kWarmupRuns,
-               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
-               kTimeCheckInterval) {
-  if (base::ThreadTicks::IsSupported())
-    base::ThreadTicks::WaitUntilInitialized();
-}
-
-void LapTimer::Reset() {
-  accumulator_ = base::TimeDelta();
-  num_laps_ = 0;
-  remaining_warmups_ = warmup_laps_;
-  remaining_no_check_laps_ = check_interval_;
-  Start();
-}
-
-void LapTimer::Start() {
-  start_time_ = Now();
-}
-
-bool LapTimer::IsWarmedUp() {
-  return remaining_warmups_ <= 0;
-}
-
-void LapTimer::NextLap() {
-  if (!IsWarmedUp()) {
-    --remaining_warmups_;
-    if (IsWarmedUp()) {
-      Start();
-    }
-    return;
-  }
-  ++num_laps_;
-  --remaining_no_check_laps_;
-  if (!remaining_no_check_laps_) {
-    base::TimeDelta now = Now();
-    accumulator_ += now - start_time_;
-    start_time_ = now;
-    remaining_no_check_laps_ = check_interval_;
-  }
-}
-
-bool LapTimer::HasTimeLimitExpired() {
-  return accumulator_ >= time_limit_;
-}
-
-bool LapTimer::HasTimedAllLaps() {
-  return !(num_laps_ % check_interval_);
-}
-
-float LapTimer::MsPerLap() {
-  DCHECK(HasTimedAllLaps());
-  return accumulator_.InMillisecondsF() / num_laps_;
-}
-
-float LapTimer::LapsPerSecond() {
-  DCHECK(HasTimedAllLaps());
-  return num_laps_ / accumulator_.InSecondsF();
-}
-
-int LapTimer::NumLaps() {
-  return num_laps_;
-}
-
-}  // namespace cc
diff --git a/cc/base/lap_timer.h b/cc/base/lap_timer.h
deleted file mode 100644
index 5b27e25..0000000
--- a/cc/base/lap_timer.h
+++ /dev/null
@@ -1,67 +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 CC_BASE_LAP_TIMER_H_
-#define CC_BASE_LAP_TIMER_H_
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "cc/base/base_export.h"
-
-namespace cc {
-
-// LapTimer is used to calculate average times per "Lap" in perf tests.
-// Current() reports the time since the last call to Start().
-// Store() adds the time since the last call to Start() to the accumulator, and
-// resets the start time to now. Stored() returns the accumulated time.
-// NextLap increments the lap counter, used in counting the per lap averages.
-// If you initialize the LapTimer with a non zero warmup_laps, it will ignore
-// the times for that many laps at the start.
-// If you set the time_limit then you can use HasTimeLimitExpired() to see if
-// the current accumulated time has crossed that threshold, with an optimization
-// that it only tests this every check_interval laps.
-class CC_BASE_EXPORT LapTimer {
- public:
-  LapTimer(int warmup_laps, base::TimeDelta time_limit, int check_interval);
-  // Create LapTimer with sensible default values.
-  LapTimer();
-  // Resets the timer back to it's starting state.
-  void Reset();
-  // Sets the start point to now.
-  void Start();
-  // Returns true if there are no more warmup laps to do.
-  bool IsWarmedUp();
-  // Advance the lap counter and update the accumulated time.
-  // The accumulated time is only updated every check_interval laps.
-  // If accumulating then the start point will also be updated.
-  void NextLap();
-  // Returns true if the stored time has exceeded the time limit specified.
-  // May cause a call to Store().
-  bool HasTimeLimitExpired();
-  // Returns true if all lap times have been timed. Only true every n'th
-  // lap, where n = check_interval.
-  bool HasTimedAllLaps();
-  // The average milliseconds per lap.
-  float MsPerLap();
-  // The number of laps per second.
-  float LapsPerSecond();
-  // The number of laps recorded.
-  int NumLaps();
-
- private:
-  base::TimeDelta start_time_;
-  base::TimeDelta accumulator_;
-  int num_laps_;
-  int warmup_laps_;
-  int remaining_warmups_;
-  int remaining_no_check_laps_;
-  base::TimeDelta time_limit_;
-  int check_interval_;
-
-  DISALLOW_COPY_AND_ASSIGN(LapTimer);
-};
-
-}  // namespace cc
-
-#endif  // CC_BASE_LAP_TIMER_H_
diff --git a/cc/base/rtree_perftest.cc b/cc/base/rtree_perftest.cc
index 70d44bc..3b12bf5 100644
--- a/cc/base/rtree_perftest.cc
+++ b/cc/base/rtree_perftest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/base/rtree.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
@@ -85,7 +85,7 @@
   }
 
  protected:
-  LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(RTreePerfTest, Construct) {
diff --git a/cc/benchmarks/rasterize_and_record_benchmark.cc b/cc/benchmarks/rasterize_and_record_benchmark.cc
index 4b78bfd..844f66a 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark.cc
@@ -13,8 +13,8 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/timer/lap_timer.h"
 #include "base/values.h"
-#include "cc/base/lap_timer.h"
 #include "cc/benchmarks/rasterize_and_record_benchmark_impl.h"
 #include "cc/layers/content_layer_client.h"
 #include "cc/layers/layer.h"
@@ -31,7 +31,7 @@
 
 const int kDefaultRecordRepeatCount = 100;
 
-// Parameters for LapTimer.
+// Parameters for base::LapTimer.
 const int kTimeLimitMillis = 1;
 const int kWarmupRuns = 0;
 const int kTimeCheckInterval = 1;
@@ -158,9 +158,9 @@
     for (int i = 0; i < record_repeat_count_; ++i) {
       // Run for a minimum amount of time to avoid problems with timer
       // quantization when the layer is very small.
-      LapTimer timer(kWarmupRuns,
-                     base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
-                     kTimeCheckInterval);
+      base::LapTimer timer(kWarmupRuns,
+                           base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+                           kTimeCheckInterval);
 
       do {
         display_list = painter->PaintContentsToDisplayList(painting_control);
@@ -179,8 +179,7 @@
 
         timer.NextLap();
       } while (!timer.HasTimeLimitExpired());
-      base::TimeDelta duration =
-          base::TimeDelta::FromMillisecondsD(timer.MsPerLap());
+      base::TimeDelta duration = timer.TimePerLap();
       if (duration < min_time)
         min_time = duration;
     }
diff --git a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
index d3652f0d..cf7f706 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -9,8 +9,8 @@
 #include <algorithm>
 #include <limits>
 
+#include "base/timer/lap_timer.h"
 #include "base/values.h"
-#include "cc/base/lap_timer.h"
 #include "cc/layers/layer_impl.h"
 #include "cc/layers/picture_layer_impl.h"
 #include "cc/raster/playback_image_provider.h"
@@ -34,7 +34,7 @@
                   size_t repeat_count,
                   base::TimeDelta* min_time,
                   bool* is_solid_color) {
-  // Parameters for LapTimer.
+  // Parameters for base::LapTimer.
   const int kTimeLimitMillis = 1;
   const int kWarmupRuns = 0;
   const int kTimeCheckInterval = 1;
@@ -43,9 +43,9 @@
   for (size_t i = 0; i < repeat_count; ++i) {
     // Run for a minimum amount of time to avoid problems with timer
     // quantization when the layer is very small.
-    LapTimer timer(kWarmupRuns,
-                   base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
-                   kTimeCheckInterval);
+    base::LapTimer timer(kWarmupRuns,
+                         base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+                         kTimeCheckInterval);
     SkColor color = SK_ColorTRANSPARENT;
     gfx::Rect layer_rect =
         gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
@@ -78,8 +78,7 @@
 
       timer.NextLap();
     } while (!timer.HasTimeLimitExpired());
-    base::TimeDelta duration =
-        base::TimeDelta::FromMillisecondsD(timer.MsPerLap());
+    base::TimeDelta duration = timer.TimePerLap();
     if (duration < *min_time)
       *min_time = duration;
   }
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 837b7e0..d2e95ca 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -126,6 +126,10 @@
 
   void PopulateSharedQuadState(viz::SharedQuadState* state,
                                bool contents_opaque) const;
+
+  // If using this, you need to override GetEnclosingRectInTargetSpace() to
+  // use GetScaledEnclosingRectInTargetSpace(). To do otherwise may result in
+  // inconsistent values, and drawing/clipping problems.
   void PopulateScaledSharedQuadState(viz::SharedQuadState* state,
                                      float layer_to_content_scale_x,
                                      float layer_to_content_scale_y,
@@ -425,6 +429,10 @@
   // for layers that provide it.
   virtual Region GetInvalidationRegionForDebugging();
 
+  // If you override this, and are making use of
+  // PopulateScaledSharedQuadState(), make sure you call
+  // GetScaledEnclosingRectInTargetSpace(). See comment for
+  // PopulateScaledSharedQuadState().
   virtual gfx::Rect GetEnclosingRectInTargetSpace() const;
 
   void UpdatePropertyTreeForAnimationIfNeeded(ElementId element_id);
@@ -476,6 +484,10 @@
                              SkColor color,
                              float width) const;
 
+  // Returns the bounds of this layer in target space when scaled by |scale|.
+  // This function scales in the same way as
+  // PopulateScaledSharedQuadStateQuadState(). See
+  // PopulateScaledSharedQuadStateQuadState() for more details.
   gfx::Rect GetScaledEnclosingRectInTargetSpace(float scale) const;
 
  private:
@@ -551,6 +563,8 @@
   DrawMode current_draw_mode_;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(SurfaceLayerImplTest, GetEnclosingRectInTargetSpace);
+
   PropertyTrees* GetPropertyTrees() const;
   ClipTree& GetClipTree() const;
   EffectTree& GetEffectTree() const;
diff --git a/cc/layers/layer_perftest.cc b/cc/layers/layer_perftest.cc
index a0bee52..c217188 100644
--- a/cc/layers/layer_perftest.cc
+++ b/cc/layers/layer_perftest.cc
@@ -5,8 +5,8 @@
 #include "cc/layers/layer.h"
 
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/timer/lap_timer.h"
 #include "cc/animation/animation_host.h"
-#include "cc/base/lap_timer.h"
 #include "cc/test/fake_impl_task_runner_provider.h"
 #include "cc/test/fake_layer_tree_host.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -53,7 +53,7 @@
   FakeLayerTreeHostClient fake_client_;
   std::unique_ptr<AnimationHost> animation_host_;
   std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
-  LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(LayerPerfTest, PushPropertiesTo) {
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc
index 36425eb..281beab 100644
--- a/cc/layers/picture_layer_impl_perftest.cc
+++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -6,7 +6,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/test/fake_impl_task_runner_provider.h"
 #include "cc/test/fake_layer_tree_frame_sink.h"
 #include "cc/test/fake_layer_tree_host_impl.h"
@@ -174,7 +174,7 @@
   std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink_;
   FakeLayerTreeHostImpl host_impl_;
   FakePictureLayerImpl* pending_layer_;
-  LapTimer timer_;
+  base::LapTimer timer_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(PictureLayerImplPerfTest);
diff --git a/cc/layers/surface_layer_impl.cc b/cc/layers/surface_layer_impl.cc
index 36c4dc3..9f9bd25 100644
--- a/cc/layers/surface_layer_impl.cc
+++ b/cc/layers/surface_layer_impl.cc
@@ -152,6 +152,11 @@
   return true;
 }
 
+gfx::Rect SurfaceLayerImpl::GetEnclosingRectInTargetSpace() const {
+  return GetScaledEnclosingRectInTargetSpace(
+      layer_tree_impl()->device_scale_factor());
+}
+
 viz::SurfaceDrawQuad* SurfaceLayerImpl::CreateSurfaceDrawQuad(
     viz::RenderPass* render_pass,
     const viz::SurfaceRange& surface_range) {
diff --git a/cc/layers/surface_layer_impl.h b/cc/layers/surface_layer_impl.h
index 64ff796..d00a8ba 100644
--- a/cc/layers/surface_layer_impl.h
+++ b/cc/layers/surface_layer_impl.h
@@ -69,6 +69,7 @@
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
   bool is_surface_layer() const override;
+  gfx::Rect GetEnclosingRectInTargetSpace() const override;
 
  protected:
   SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id, UpdateSubmissionStateCB);
diff --git a/cc/layers/surface_layer_impl_unittest.cc b/cc/layers/surface_layer_impl_unittest.cc
index c0fc9e7..531445a 100644
--- a/cc/layers/surface_layer_impl_unittest.cc
+++ b/cc/layers/surface_layer_impl_unittest.cc
@@ -262,4 +262,31 @@
 }
 
 }  // namespace
+
+// This test is outside the anonymous namespace so that it can be a friend.
+TEST(SurfaceLayerImplTest, GetEnclosingRectInTargetSpace) {
+  gfx::Size layer_size(902, 1000);
+  gfx::Size viewport_size(902, 1000);
+  LayerTestCommon::LayerImplTest impl;
+  SurfaceLayerImpl* surface_layer_impl =
+      impl.AddChildToRoot<SurfaceLayerImpl>();
+  surface_layer_impl->SetBounds(layer_size);
+  surface_layer_impl->SetDrawsContent(true);
+
+  // A device scale of 1.33 and transform of 1.5 were chosen as they produce
+  // different results when rounding at each stage, vs applying a single
+  // transform.
+  gfx::Transform transform;
+  transform.Scale(1.5, 1.5);
+  impl.host_impl()->active_tree()->SetDeviceScaleFactor(1.33);
+  impl.CalcDrawProps(viewport_size);
+  surface_layer_impl->draw_properties().target_space_transform = transform;
+
+  // GetEnclosingRectInTargetSpace() and GetScaledEnclosingRectInTargetSpace()
+  // should return the same value, otherwise we may not damage the right
+  // pixels.
+  EXPECT_EQ(surface_layer_impl->GetScaledEnclosingRectInTargetSpace(1.33),
+            surface_layer_impl->GetEnclosingRectInTargetSpace());
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_op_perftest.cc b/cc/paint/paint_op_perftest.cc
index d137118..6dacf3eb 100644
--- a/cc/paint/paint_op_perftest.cc
+++ b/cc/paint/paint_op_perftest.cc
@@ -6,7 +6,7 @@
 
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/paint/paint_op_buffer.h"
 #include "cc/paint/paint_op_buffer_serializer.h"
 #include "cc/test/test_options_provider.h"
@@ -104,7 +104,7 @@
   }
 
  protected:
-  LapTimer timer_;
+  base::LapTimer timer_;
   std::unique_ptr<char, base::AlignedFreeDeleter> serialized_data_;
   std::unique_ptr<char, base::AlignedFreeDeleter> deserialized_data_;
 };
diff --git a/cc/raster/raster_buffer_provider_perftest.cc b/cc/raster/raster_buffer_provider_perftest.cc
index dc5fd57..6eb36031 100644
--- a/cc/raster/raster_buffer_provider_perftest.cc
+++ b/cc/raster/raster_buffer_provider_perftest.cc
@@ -8,8 +8,8 @@
 #include "base/macros.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
+#include "base/timer/lap_timer.h"
 #include "build/build_config.h"
-#include "cc/base/lap_timer.h"
 #include "cc/raster/bitmap_raster_buffer_provider.h"
 #include "cc/raster/gpu_raster_buffer_provider.h"
 #include "cc/raster/one_copy_raster_buffer_provider.h"
@@ -340,7 +340,7 @@
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   std::unique_ptr<ResourcePool> resource_pool_;
   std::unique_ptr<SynchronousTaskGraphRunner> task_graph_runner_;
-  LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 class RasterBufferProviderPerfTest
diff --git a/cc/raster/task_graph_runner_perftest.cc b/cc/raster/task_graph_runner_perftest.cc
index 8a67451..36ee90fa 100644
--- a/cc/raster/task_graph_runner_perftest.cc
+++ b/cc/raster/task_graph_runner_perftest.cc
@@ -11,8 +11,8 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
+#include "base/timer/lap_timer.h"
 #include "cc/base/completion_event.h"
-#include "cc/base/lap_timer.h"
 #include "cc/raster/synchronous_task_graph_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
@@ -274,7 +274,7 @@
   // minimal additional complexity over the TaskGraphWorkQueue helpers.
   std::unique_ptr<SynchronousTaskGraphRunner> task_graph_runner_;
   NamespaceToken namespace_token_;
-  LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(TaskGraphRunnerPerfTest, BuildTaskGraph) {
diff --git a/cc/tiles/gpu_image_decode_cache_perftest.cc b/cc/tiles/gpu_image_decode_cache_perftest.cc
index 819d16bb..5d0f2e6 100644
--- a/cc/tiles/gpu_image_decode_cache_perftest.cc
+++ b/cc/tiles/gpu_image_decode_cache_perftest.cc
@@ -4,7 +4,7 @@
 
 #include <vector>
 
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_image_builder.h"
 #include "cc/raster/tile_task.h"
@@ -87,7 +87,7 @@
     }
   }
 
-  LapTimer timer_;
+  base::LapTimer timer_;
   scoped_refptr<TestInProcessContextProvider> context_provider_;
   std::unique_ptr<GpuImageDecodeCache> cache_;
 };
diff --git a/cc/tiles/software_image_decode_cache_perftest.cc b/cc/tiles/software_image_decode_cache_perftest.cc
index 5f69317..f873c977 100644
--- a/cc/tiles/software_image_decode_cache_perftest.cc
+++ b/cc/tiles/software_image_decode_cache_perftest.cc
@@ -4,7 +4,7 @@
 
 #include <vector>
 
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_image_builder.h"
 #include "cc/raster/tile_task.h"
@@ -82,7 +82,7 @@
   }
 
  private:
-  LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(SoftwareImageDecodeCachePerfTest, FromDrawImage) {
diff --git a/cc/tiles/tile_manager_perftest.cc b/cc/tiles/tile_manager_perftest.cc
index 8a875845..e6334235e 100644
--- a/cc/tiles/tile_manager_perftest.cc
+++ b/cc/tiles/tile_manager_perftest.cc
@@ -10,7 +10,7 @@
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/raster/raster_buffer.h"
 #include "cc/test/fake_impl_task_runner_provider.h"
 #include "cc/test/fake_layer_tree_frame_sink.h"
@@ -292,7 +292,7 @@
   TileManager* tile_manager() { return host_impl()->tile_manager(); }
 
  protected:
-  LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 // Failing.  https://crbug.com/792995
diff --git a/cc/trees/layer_tree_host_common_perftest.cc b/cc/trees/layer_tree_host_common_perftest.cc
index 561cdd1..2c1c4ab 100644
--- a/cc/trees/layer_tree_host_common_perftest.cc
+++ b/cc/trees/layer_tree_host_common_perftest.cc
@@ -13,7 +13,7 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/layers/layer.h"
 #include "cc/test/fake_content_layer_client.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -62,17 +62,13 @@
 
   void AfterTest() override {
     CHECK(!test_name_.empty()) << "Must SetTestName() before TearDown().";
-    perf_test::PrintResult("calc_draw_props_time",
-                           "",
-                           test_name_,
-                           1000 * timer_.MsPerLap(),
-                           "us",
-                           true);
+    perf_test::PrintResult("calc_draw_props_time", "", test_name_,
+                           timer_.TimePerLap().InMicrosecondsF(), "us", true);
   }
 
  protected:
   FakeContentLayerClient content_layer_client_;
-  LapTimer timer_;
+  base::LapTimer timer_;
   std::string test_name_;
   std::string json_;
 };
diff --git a/cc/trees/layer_tree_host_perftest.cc b/cc/trees/layer_tree_host_perftest.cc
index 0e2f8b73..35953305 100644
--- a/cc/trees/layer_tree_host_perftest.cc
+++ b/cc/trees/layer_tree_host_perftest.cc
@@ -14,7 +14,7 @@
 #include "base/path_service.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/layers/nine_patch_layer.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/layers/texture_layer.h"
@@ -108,16 +108,18 @@
   void AfterTest() override {
     CHECK(!test_name_.empty()) << "Must SetTestName() before AfterTest().";
     perf_test::PrintResult("layer_tree_host_frame_time", "", test_name_,
-                           1000 * draw_timer_.MsPerLap(), "us", true);
+                           draw_timer_.TimePerLap().InMicrosecondsF(), "us",
+                           true);
     if (measure_commit_cost_) {
       perf_test::PrintResult("layer_tree_host_commit_time", "", test_name_,
-                             1000 * commit_timer_.MsPerLap(), "us", true);
+                             commit_timer_.TimePerLap().InMicrosecondsF(), "us",
+                             true);
     }
   }
 
  protected:
-  LapTimer draw_timer_;
-  LapTimer commit_timer_;
+  base::LapTimer draw_timer_;
+  base::LapTimer commit_timer_;
 
   std::string test_name_;
   FakeContentLayerClient fake_content_layer_client_;
diff --git a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
index ce3f589..d96094be 100644
--- a/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
+++ b/chrome/android/features/vr/java/src/org/chromium/chrome/browser/vr/VrShell.java
@@ -773,7 +773,9 @@
             removeVrRootView();
         }
 
-        mActivity.getFullscreenManager().exitPersistentFullscreenMode();
+        if (!mActivity.isActivityFinishingOrDestroyed()) {
+            mActivity.getFullscreenManager().exitPersistentFullscreenMode();
+        }
         reparentAllTabs(mActivity.getWindowAndroid());
         if (mNativeVrShell != 0) {
             nativeDestroy(mNativeVrShell);
diff --git a/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_details_bg.xml b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_details_bg.xml
index 9ce233e..67ed25d0 100644
--- a/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_details_bg.xml
+++ b/chrome/android/java/res_autofill_assistant/drawable/autofill_assistant_details_bg.xml
@@ -7,7 +7,5 @@
     android:shape="rectangle">
     <corners
         android:radius="8dp" />
-    <stroke
-        android:width="1dp"
-        android:color="@color/hairline_stroke_color" />
+    <solid android:color="@color/modern_secondary_color"/>
 </shape>
diff --git a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_details.xml b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_details.xml
index c5cddbd..d9ceff0 100644
--- a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_details.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_details.xml
@@ -42,7 +42,7 @@
                 android:id="@+id/details_line1"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:textAppearance="@style/TextAppearance.AssistantBlackCaption"
+                android:textAppearance="@style/TextAppearance.AssistantGreyCaption"
                 android:singleLine="true"
                 android:requiresFadingEdge="horizontal"
                 android:fadingEdgeLength="20dp"
@@ -54,7 +54,7 @@
                 android:id="@+id/details_line2"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:textAppearance="@style/TextAppearance.AssistantBlackCaption"
+                android:textAppearance="@style/TextAppearance.AssistantGreyCaption"
                 android:singleLine="true"
                 android:requiresFadingEdge="horizontal"
                 android:fadingEdgeLength="20dp"
diff --git a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_onboarding.xml b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_onboarding.xml
index 955fc64..86d9e46 100644
--- a/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_onboarding.xml
+++ b/chrome/android/java/res_autofill_assistant/layout/autofill_assistant_onboarding.xml
@@ -21,8 +21,6 @@
             android:layout_height="wrap_content"
             android:gravity="center_horizontal"
             android:orientation="vertical"
-            android:paddingTop="20dp"
-            android:paddingBottom="20dp"
             android:paddingStart="24dp"
             android:paddingEnd="24dp">
 
@@ -33,7 +31,6 @@
                 android:layout_height="141dp"
                 android:scaleType="centerCrop"
                 android:src="@drawable/onboarding_background"
-                android:paddingTop="10dp"
                 android:paddingBottom="15dp"/>
 
             <!-- Title (e.g., 'Fast Checkout')-->
@@ -129,5 +126,4 @@
         android:text="@string/init_ok"
         style="FilledButton" />
     </LinearLayout>
-    <Space android:layout_width="0dp" android:layout_height="24dp"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res_autofill_assistant/values-v17/colors.xml b/chrome/android/java/res_autofill_assistant/values-v17/colors.xml
index ee06f025..3f38ff3 100644
--- a/chrome/android/java/res_autofill_assistant/values-v17/colors.xml
+++ b/chrome/android/java/res_autofill_assistant/values-v17/colors.xml
@@ -9,6 +9,7 @@
 
     Please see src/ui/android/java/res/values/colors.xml for the shared common colors.
     -->
+    <color name="autofill_assistant_caption_grey">@color/modern_grey_800</color>
     <color name="autofill_assistant_light_grey">@color/modern_grey_300</color>
     <color name="autofill_assistant_light_blue">#E9F0FD</color>
 </resources>
diff --git a/chrome/android/java/res_autofill_assistant/values-v17/styles.xml b/chrome/android/java/res_autofill_assistant/values-v17/styles.xml
index 7eadf5ac..f9f39f4 100644
--- a/chrome/android/java/res_autofill_assistant/values-v17/styles.xml
+++ b/chrome/android/java/res_autofill_assistant/values-v17/styles.xml
@@ -16,6 +16,10 @@
     <style name="TextAppearance.AssistantBlackCaption" parent="TextAppearance.BlackCaption">
         <item name="android:fontFamily">@font/accent_font</item>
     </style>
+    <style name="TextAppearance.AssistantGreyCaption" parent="TextAppearance.BlackCaption">
+        <item name="android:textColor">@color/autofill_assistant_caption_grey</item>
+        <item name="android:fontFamily">@font/accent_font</item>
+    </style>
     <style name="TextAppearance.AssistantBlackBody" parent="TextAppearance.BlackBody">
         <item name="android:fontFamily">@font/accent_font</item>
     </style>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 5e9216a..5f1f5a0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1288,6 +1288,9 @@
         public void processUrlViewIntent(String url, String referer, String headers,
                 @TabOpenType int tabOpenType, String externalAppId, int tabIdToBringToFront,
                 boolean hasUserGesture, Intent intent) {
+            if (isActivityFinishingOrDestroyed()) {
+                return;
+            }
             if (isFromChrome(intent, externalAppId)) {
                 RecordUserAction.record("MobileTabbedModeViewIntentFromChrome");
             } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index 2e286c1..b60f3e2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -32,6 +32,7 @@
     private static final int BOTTOM_BAR_WITHOUT_INDICATOR_PADDING_TOP_DP = 16;
 
     private final ViewGroup mBottomBarView;
+    private final ViewGroup mBottomBarContainerView;
     private final View mSwipeIndicatorView;
     private final BottomSheetBehavior mBottomBarBehavior;
 
@@ -47,6 +48,8 @@
 
     AssistantBottomBarCoordinator(Context context, View assistantView, AssistantModel model) {
         mBottomBarView = assistantView.findViewById(R.id.autofill_assistant_bottombar);
+        mBottomBarContainerView =
+                mBottomBarView.findViewById(R.id.autofill_assistant_bottombar_container);
         mSwipeIndicatorView = mBottomBarView.findViewById(R.id.swipe_indicator);
         mBottomBarBehavior = BottomSheetBehavior.from(mBottomBarView);
 
@@ -66,12 +69,10 @@
         mActionsCoordinator = new AssistantCarouselCoordinator(context, model.getActionsModel());
 
         // Add child views to bottom bar container.
-        ViewGroup bottomBarContainer =
-                mBottomBarView.findViewById(R.id.autofill_assistant_bottombar_container);
-        bottomBarContainer.addView(mDetailsCoordinator.getView());
-        bottomBarContainer.addView(mPaymentRequestCoordinator.getView());
-        bottomBarContainer.addView(mSuggestionsCoordinator.getView());
-        bottomBarContainer.addView(mActionsCoordinator.getView());
+        mBottomBarContainerView.addView(mDetailsCoordinator.getView());
+        mBottomBarContainerView.addView(mPaymentRequestCoordinator.getView());
+        mBottomBarContainerView.addView(mSuggestionsCoordinator.getView());
+        mBottomBarContainerView.addView(mActionsCoordinator.getView());
 
         // We set the horizontal margins of the details and payment request. We don't set a padding
         // to the container as we want the carousels children to be scrolled at the limit of the
@@ -89,6 +90,13 @@
     }
 
     /**
+     * Returns the view container inside the bottom bar view.
+     */
+    public ViewGroup getContainerView() {
+        return mBottomBarContainerView;
+    }
+
+    /**
      * Make sure the bottom bar is expanded and text is visible.
      */
     public void expand() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index 0917d910d..ae7b40e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -107,7 +107,7 @@
         // Disable swiping for the onboarding because it interferes with letting the user scroll
         // the onboarding contents.
         mBottomBarCoordinator.allowSwipingBottomSheet(false);
-        AssistantOnboardingCoordinator.show(mActivity, mBottomBarCoordinator.getView())
+        AssistantOnboardingCoordinator.show(mActivity, mBottomBarCoordinator.getContainerView())
                 .then(accepted -> {
                     mBottomBarCoordinator.allowSwipingBottomSheet(true);
                     if (!accepted) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java
index 31af6008..e6b9ed9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java
@@ -13,12 +13,14 @@
  * A chip to display to the user.
  */
 public class AssistantChip {
-    @IntDef({Type.CHIP_ASSISTIVE, Type.BUTTON_FILLED_BLUE, Type.BUTTON_HAIRLINE})
+    @IntDef({Type.CHIP_ASSISTIVE, Type.BUTTON_FILLED_BLUE, Type.BUTTON_HAIRLINE,
+            Type.BUTTON_FILLED_DISABLED})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {
         int CHIP_ASSISTIVE = 0;
         int BUTTON_FILLED_BLUE = 1;
         int BUTTON_HAIRLINE = 2;
+        int BUTTON_FILLED_DISABLED = 3;
     }
 
     private final @Type int mType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
index 6e8bda68..f83d313 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java
@@ -31,6 +31,7 @@
             case AssistantChip.Type.CHIP_ASSISTIVE:
                 resId = R.layout.autofill_assistant_chip_assistive;
                 break;
+            case AssistantChip.Type.BUTTON_FILLED_DISABLED:
             case AssistantChip.Type.BUTTON_FILLED_BLUE:
                 resId = R.layout.autofill_assistant_button_filled;
                 break;
@@ -40,8 +41,13 @@
             default:
                 assert false : "Unsupported view type " + viewType;
         }
-        return new AssistantChipViewHolder(
-                (TextView) layoutInflater.inflate(resId, /* root= */ null));
+
+        TextView view = (TextView) layoutInflater.inflate(resId, /* root= */ null);
+        if (viewType == AssistantChip.Type.BUTTON_FILLED_DISABLED) {
+            view.setEnabled(false);
+        }
+
+        return new AssistantChipViewHolder(view);
     }
 
     public void bind(AssistantChip chip) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetails.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetails.java
index f403e76b..3343a63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetails.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetails.java
@@ -24,6 +24,7 @@
 
     private final String mTitle;
     private final String mImageUrl;
+    private final boolean mShowImagePlaceholder;
     @Nullable
     private final Date mDate;
     private final String mDescriptionLine1;
@@ -37,7 +38,7 @@
     /** Whether the second description line should be highlighted. */
     private boolean mHighlightLine2;
     /** Whether empty fields should have the animated placeholder background. */
-    private final boolean mShowPlaceholdersForEmptyFields;
+    private final boolean mAnimatePlaceholders;
     /**
      * The correctly formatted price for the client locale, including the currency.
      * Example: '$20.50' or '20.50 €'.
@@ -46,14 +47,14 @@
     /** An optional price label, such as 'Estimated Total incl. VAT'. */
     private final String mTotalPriceLabel;
 
-    public AssistantDetails(String title, String imageUrl, String totalPriceLabel,
-            String totalPrice, @Nullable Date date, String descriptionLine1,
+    public AssistantDetails(String title, String imageUrl, boolean showImagePlaceholder,
+            String totalPriceLabel, String totalPrice, @Nullable Date date, String descriptionLine1,
             String descriptionLine2, boolean userApprovalRequired, boolean highlightTitle,
-            boolean highlightLine1, boolean highlightLine2,
-            boolean showPlaceholdersForEmptyFields) {
+            boolean highlightLine1, boolean highlightLine2, boolean animatePlaceholders) {
         this.mTotalPriceLabel = totalPriceLabel;
         this.mTitle = title;
         this.mImageUrl = imageUrl;
+        this.mShowImagePlaceholder = showImagePlaceholder;
         this.mTotalPrice = totalPrice;
         this.mDate = date;
         this.mDescriptionLine1 = descriptionLine1;
@@ -63,7 +64,7 @@
         this.mHighlightTitle = highlightTitle;
         this.mHighlightLine1 = highlightLine1;
         this.mHighlightLine2 = highlightLine2;
-        this.mShowPlaceholdersForEmptyFields = showPlaceholdersForEmptyFields;
+        this.mAnimatePlaceholders = animatePlaceholders;
     }
 
     String getTitle() {
@@ -74,6 +75,10 @@
         return mImageUrl;
     }
 
+    boolean getShowImagePlaceholder() {
+        return mShowImagePlaceholder;
+    }
+
     @Nullable
     Date getDate() {
         return mDate;
@@ -111,19 +116,20 @@
         return mHighlightLine2;
     }
 
-    boolean getShowPlaceholdersForEmptyFields() {
-        return mShowPlaceholdersForEmptyFields;
+    boolean getAnimatePlaceholders() {
+        return mAnimatePlaceholders;
     }
 
     /**
      * Create details with the given values.
      */
     @CalledByNative
-    private static AssistantDetails create(String title, String imageUrl, String totalPriceLabel,
-            String totalPrice, String datetime, long year, int month, int day, int hour, int minute,
-            int second, String descriptionLine1, String descriptionLine2,
-            boolean userApprovalRequired, boolean highlightTitle, boolean highlightLine1,
-            boolean highlightLine2) {
+    private static AssistantDetails create(String title, String imageUrl,
+            boolean showImagePlaceholder, String totalPriceLabel, String totalPrice,
+            String datetime, long year, int month, int day, int hour, int minute, int second,
+            String descriptionLine1, String descriptionLine2, boolean userApprovalRequired,
+            boolean highlightTitle, boolean highlightLine1, boolean highlightLine2,
+            boolean animatePlaceholders) {
         Date date = null;
         if (year > 0 && month > 0 && day > 0 && hour >= 0 && minute >= 0 && second >= 0) {
             Calendar calendar = Calendar.getInstance();
@@ -142,8 +148,8 @@
             }
         }
 
-        return new AssistantDetails(title, imageUrl, totalPriceLabel, totalPrice, date,
-                descriptionLine1, descriptionLine2, userApprovalRequired, highlightTitle,
-                highlightLine1, highlightLine2, /* showPlaceholdersForEmptyFields= */ false);
+        return new AssistantDetails(title, imageUrl, showImagePlaceholder, totalPriceLabel,
+                totalPrice, date, descriptionLine1, descriptionLine2, userApprovalRequired,
+                highlightTitle, highlightLine1, highlightLine2, animatePlaceholders);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java
index dc9702c..bb3de57 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetailsViewBinder.java
@@ -35,10 +35,8 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 
 /**
  * This class is responsible for pushing updates to the Autofill Assistant details view. These
@@ -86,7 +84,6 @@
     private final int mPulseAnimationStartColor;
     private final int mPulseAnimationEndColor;
 
-    private final Set<View> mViewsToAnimate = new HashSet<>();
     private ValueAnimator mPulseAnimation;
 
     AssistantDetailsViewBinder(Context context) {
@@ -95,8 +92,8 @@
                 R.dimen.autofill_assistant_details_image_size);
         mImageHeight = context.getResources().getDimensionPixelSize(
                 R.dimen.autofill_assistant_details_image_size);
-        mPulseAnimationStartColor = context.getResources().getColor(R.color.modern_grey_100);
-        mPulseAnimationEndColor = context.getResources().getColor(R.color.modern_grey_50);
+        mPulseAnimationStartColor = context.getResources().getColor(R.color.modern_grey_300);
+        mPulseAnimationEndColor = context.getResources().getColor(R.color.modern_grey_200);
     }
 
     @Override
@@ -141,15 +138,15 @@
         viewHolder.mPriceView.setVisibility(
                 details.getTotalPrice().isEmpty() ? View.GONE : View.VISIBLE);
 
-        if (viewHolder.mImageView.getDrawable() == null) {
-            // Set default image if no image was set before.
-            viewHolder.mImageView.setImageDrawable(viewHolder.mDefaultImage);
-        }
-
-        setTextStyles(details, viewHolder);
-
-        // Download image and then set it in the model.
-        if (!details.getImageUrl().isEmpty()) {
+        viewHolder.mImageView.setVisibility(View.VISIBLE);
+        if (details.getImageUrl().isEmpty()) {
+            if (details.getShowImagePlaceholder()) {
+                viewHolder.mImageView.setImageDrawable(viewHolder.mDefaultImage);
+            } else {
+                viewHolder.mImageView.setVisibility(View.GONE);
+            }
+        } else {
+            // Download image and then set it in the view.
             CachedImageFetcher.getInstance().fetchImage(details.getImageUrl(),
                     CachedImageFetcher.ASSISTANT_DETAILS_UMA_CLIENT_NAME, image -> {
                         if (image != null) {
@@ -157,6 +154,8 @@
                         }
                     });
         }
+
+        setTextStyles(details, viewHolder);
     }
 
     private String makeDescriptionLine1Text(AssistantDetails details) {
@@ -208,20 +207,20 @@
         setSubtextStyle(viewHolder.mDescriptionLine2View, details.getUserApprovalRequired(),
                 details.getHighlightLine2(), viewHolder);
 
-        animateIfEmpty(
-                viewHolder.mTitleView, details.getShowPlaceholdersForEmptyFields(), viewHolder);
-        animateIfEmpty(viewHolder.mDescriptionLine1View,
-                details.getShowPlaceholdersForEmptyFields(), viewHolder);
-        animateIfEmpty(viewHolder.mDescriptionLine2View,
-                details.getShowPlaceholdersForEmptyFields(), viewHolder);
+        if (shouldStartOrContinuePlaceholderAnimation(details, viewHolder)) {
+            startOrContinuePlaceholderAnimations(viewHolder);
+        } else {
+            stopPlaceholderAnimations();
+        }
     }
 
-    private void animateIfEmpty(TextView view, boolean animate, ViewHolder viewHolder) {
-        if (animate && view.length() == 0) {
-            addViewToAnimation(view, viewHolder);
-        } else {
-            removeViewFromAnimation(view);
-        }
+    private boolean shouldStartOrContinuePlaceholderAnimation(
+            AssistantDetails details, ViewHolder viewHolder) {
+        boolean isAtLeastOneFieldEmpty = viewHolder.mTitleView.length() == 0
+                || viewHolder.mDescriptionLine1View.length() == 0
+                || viewHolder.mDescriptionLine2View.length() == 0
+                || viewHolder.mImageView.getDrawable() == viewHolder.mDefaultImage;
+        return details.getAnimatePlaceholders() && isAtLeastOneFieldEmpty;
     }
 
     private void setTitleStyle(boolean approvalRequired, boolean highlight, ViewHolder viewHolder) {
@@ -235,7 +234,9 @@
             // Normal style: bold black text.
             ApiCompatibilityUtils.setTextAppearance(
                     titleView, R.style.TextAppearance_BlackCaptionDefault);
-            titleView.setTypeface(titleView.getTypeface(), Typeface.BOLD);
+            if (highlight) {
+                titleView.setTypeface(titleView.getTypeface(), Typeface.BOLD);
+            }
         }
     }
 
@@ -244,7 +245,7 @@
         // Emphasized style.
         if (approvalRequired && highlight) {
             view.setTypeface(view.getTypeface(), Typeface.BOLD_ITALIC);
-        } else if (approvalRequired && !highlight) {
+        } else if (approvalRequired) {
             // De-emphasized style.
             view.setTextColor(ApiCompatibilityUtils.getColor(
                     mContext.getResources(), R.color.modern_grey_300));
@@ -263,39 +264,45 @@
         return roundedBitmap;
     }
 
-    private void addViewToAnimation(View view, ViewHolder viewHolder) {
-        mViewsToAnimate.add(view);
-        if (mPulseAnimation == null) {
-            mPulseAnimation =
-                    ValueAnimator.ofInt(mPulseAnimationStartColor, mPulseAnimationEndColor);
-            mPulseAnimation.setDuration(PULSING_DURATION_MS);
-            mPulseAnimation.setEvaluator(new ArgbEvaluator());
-            mPulseAnimation.setRepeatCount(ValueAnimator.INFINITE);
-            mPulseAnimation.setRepeatMode(ValueAnimator.REVERSE);
-            mPulseAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR);
-            mPulseAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    viewHolder.mTitleView.setBackgroundColor(Color.WHITE);
-                    viewHolder.mDescriptionLine1View.setBackgroundColor(Color.WHITE);
-                    viewHolder.mDescriptionLine2View.setBackgroundColor(Color.WHITE);
-                    viewHolder.mDefaultImage.setColor(mPulseAnimationStartColor);
-                }
-            });
-            mPulseAnimation.addUpdateListener(animation -> {
-                int animatedValue = (int) animation.getAnimatedValue();
-                for (View viewToAnimate : mViewsToAnimate) {
-                    viewToAnimate.setBackgroundColor(animatedValue);
-                }
-                viewHolder.mDefaultImage.setColor(animatedValue);
-            });
-            mPulseAnimation.start();
+    private void startOrContinuePlaceholderAnimations(ViewHolder viewHolder) {
+        if (mPulseAnimation != null) {
+            return;
         }
+        mPulseAnimation = ValueAnimator.ofInt(mPulseAnimationStartColor, mPulseAnimationEndColor);
+        mPulseAnimation.setDuration(PULSING_DURATION_MS);
+        mPulseAnimation.setEvaluator(new ArgbEvaluator());
+        mPulseAnimation.setRepeatCount(ValueAnimator.INFINITE);
+        mPulseAnimation.setRepeatMode(ValueAnimator.REVERSE);
+        mPulseAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR);
+        mPulseAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                viewHolder.mTitleView.setBackgroundColor(Color.TRANSPARENT);
+                viewHolder.mDescriptionLine1View.setBackgroundColor(Color.TRANSPARENT);
+                viewHolder.mDescriptionLine2View.setBackgroundColor(Color.TRANSPARENT);
+                viewHolder.mDefaultImage.setColor(Color.TRANSPARENT);
+            }
+        });
+        mPulseAnimation.addUpdateListener(animation -> {
+            int animatedValue = (int) animation.getAnimatedValue();
+            viewHolder.mTitleView.setBackgroundColor(
+                    viewHolder.mTitleView.length() == 0 ? animatedValue : Color.TRANSPARENT);
+            viewHolder.mDescriptionLine1View.setBackgroundColor(
+                    viewHolder.mDescriptionLine1View.length() == 0 ? animatedValue
+                                                                   : Color.TRANSPARENT);
+            viewHolder.mDescriptionLine2View.setBackgroundColor(
+                    viewHolder.mDescriptionLine2View.length() == 0 ? animatedValue
+                                                                   : Color.TRANSPARENT);
+            viewHolder.mDefaultImage.setColor(
+                    viewHolder.mImageView.getDrawable() == viewHolder.mDefaultImage
+                            ? animatedValue
+                            : Color.TRANSPARENT);
+        });
+        mPulseAnimation.start();
     }
 
-    private void removeViewFromAnimation(View view) {
-        mViewsToAnimate.remove(view);
-        if (mViewsToAnimate.isEmpty() && mPulseAnimation != null) {
+    private void stopPlaceholderAnimations() {
+        if (mPulseAnimation != null) {
             mPulseAnimation.cancel();
             mPulseAnimation = null;
         }
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 8c74fbe..4b2ac81b 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
@@ -9,6 +9,7 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.customtabs.CustomTabsService;
 import android.support.customtabs.CustomTabsService.Relation;
 import android.text.TextUtils;
@@ -67,7 +68,7 @@
     private final String mSignatureFingerprint;
     private final @Relation int mRelation;
     private long mNativeOriginVerifier;
-    private OriginVerificationListener mListener;
+    @Nullable private OriginVerificationListener mListener;
     private Origin mOrigin;
 
     /**
@@ -274,6 +275,13 @@
         }
     }
 
+    /**
+     * Removes the verification listener, but finishes the ongoing verification process, if any.
+     */
+    public void removeListener() {
+        mListener = null;
+    }
+
     private static boolean shouldOverrideVerification(String packageName, Origin origin,
             int relation) {
         if (sVerificationOverrides.get() == null) return false;
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 ef411bb..7f9f52a 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
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.customtabs.TabObserverRegistrar;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -40,7 +41,7 @@
  * {@link TrustedWebActivityModel} accordingly.
  */
 @ActivityScope
-public class TrustedWebActivityVerifier implements NativeInitObserver {
+public class TrustedWebActivityVerifier implements NativeInitObserver, Destroyable {
     /** The Digital Asset Link relationship used for Trusted Web Activities. */
     private final static int RELATIONSHIP = CustomTabsService.RELATION_HANDLE_ALL_URLS;
 
@@ -208,6 +209,12 @@
         }
     }
 
+    @Override
+    public void destroy() {
+        // Verification may finish after activity is destroyed.
+        mOriginVerifier.removeListener();
+    }
+
     /**
      * Register that we have Chrome data relevant to the Client app.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java
index 02752afc..4321d696 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/TrustedWebActivityToolbarView.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel.TOOLBAR_HIDDEN;
 
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel;
 import org.chromium.chrome.browser.customtabs.CustomTabBrowserControlsVisibilityDelegate;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
@@ -27,21 +28,27 @@
     private final Lazy<ChromeFullscreenManager> mFullscreenManager;
     private final CustomTabBrowserControlsVisibilityDelegate mControlsVisibilityDelegate;
     private final TrustedWebActivityModel mModel;
+    private final ChromeActivity mActivity;
 
     private int mControlsHidingToken = FullscreenManager.INVALID_TOKEN;
 
     @Inject
     public TrustedWebActivityToolbarView(Lazy<ChromeFullscreenManager> fullscreenManager,
             CustomTabBrowserControlsVisibilityDelegate controlsVisibilityDelegate,
-            TrustedWebActivityModel model) {
+            TrustedWebActivityModel model, ChromeActivity activity) {
         mFullscreenManager = fullscreenManager;
         mControlsVisibilityDelegate = controlsVisibilityDelegate;
         mModel = model;
+        mActivity = activity;
         mModel.addObserver(this);
     }
 
     @Override
     public void onPropertyChanged(PropertyObservable<PropertyKey> observable, PropertyKey key) {
+        if (mActivity.isActivityFinishingOrDestroyed()) {
+            assert false : "Tried to change toolbar visibility when activity is destroyed";
+            return;
+        }
         if (key != TOOLBAR_HIDDEN) return;
 
         boolean hide = mModel.get(TOOLBAR_HIDDEN);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
index 97d0336..8557349 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -1186,7 +1186,7 @@
      * Wraps calling extraCallback in a try/catch so exceptions thrown by the host app don't crash
      * Chrome. See https://crbug.com/517023.
      */
-    private boolean safeExtraCallback(
+    protected boolean safeExtraCallback(
             CustomTabsSessionToken session, String callbackName, @Nullable Bundle args) {
         CustomTabsCallback callback = mClientManager.getCallbackForSession(session);
         if (callback == null) return false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementScreenHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementScreenHelper.java
index efcfd6ee..9e5359b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementScreenHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/AccountManagementScreenHelper.java
@@ -4,35 +4,36 @@
 
 package org.chromium.chrome.browser.signin;
 
+import android.app.Activity;
 import android.content.Intent;
-import android.provider.Settings;
+import android.support.annotation.Nullable;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileAccountManagementMetrics;
+import org.chromium.chrome.browser.util.IntentUtils;
+import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.GAIAServiceType;
+import org.chromium.ui.base.WindowAndroid;
 
 /**
  * Stub entry points and implementation interface for the account management fragment delegate.
  */
 public class AccountManagementScreenHelper {
-    private static final String EXTRA_ACCOUNT_TYPES = "account_types";
-    private static final String EXTRA_VALUE_GOOGLE_ACCOUNTS = "com.google";
-
     @CalledByNative
     private static void openAccountManagementScreen(
-            Profile profile, @GAIAServiceType int gaiaServiceType) {
+            WindowAndroid windowAndroid, @GAIAServiceType int gaiaServiceType) {
         ThreadUtils.assertOnUiThread();
 
-        if (gaiaServiceType == GAIAServiceType.GAIA_SERVICE_TYPE_SIGNUP) {
-            openAndroidAccountCreationScreen();
-            return;
-        }
-
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)) {
+            if (gaiaServiceType == GAIAServiceType.GAIA_SERVICE_TYPE_SIGNUP
+                    || gaiaServiceType == GAIAServiceType.GAIA_SERVICE_TYPE_ADDSESSION) {
+                startAddAccountActivity(windowAndroid, gaiaServiceType);
+                return;
+            }
+
             SigninUtils.openSettingsForAllAccounts(ContextUtils.getApplicationContext());
             return;
         }
@@ -41,20 +42,22 @@
     }
 
     /**
-     * Opens the Android account manager for adding or creating a Google account.
+     * Tries starting an Activity to add a Google account to the device. If this activity cannot
+     * be started, opens "Accounts" page in the Android Settings app.
      */
-    private static void openAndroidAccountCreationScreen() {
-        logEvent(ProfileAccountManagementMetrics.DIRECT_ADD_ACCOUNT,
-                GAIAServiceType.GAIA_SERVICE_TYPE_SIGNUP);
+    private static void startAddAccountActivity(
+            WindowAndroid windowAndroid, @GAIAServiceType int gaiaServiceTypeSignup) {
+        logEvent(ProfileAccountManagementMetrics.DIRECT_ADD_ACCOUNT, gaiaServiceTypeSignup);
 
-        Intent createAccountIntent = new Intent(Settings.ACTION_ADD_ACCOUNT);
-        createAccountIntent.putExtra(
-                EXTRA_ACCOUNT_TYPES, new String[]{EXTRA_VALUE_GOOGLE_ACCOUNTS});
-        createAccountIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
-                | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
-        ContextUtils.getApplicationContext().startActivity(createAccountIntent);
+        AccountManagerFacade.get().createAddAccountIntent((@Nullable Intent intent) -> {
+            Activity activity = windowAndroid.getActivity().get();
+            if (intent == null || activity == null
+                    || !IntentUtils.safeStartActivity(activity, intent)) {
+                // Failed to create or show an intent, open settings for all accounts so
+                // the user has a chance to create an account manually.
+                SigninUtils.openSettingsForAllAccounts(ContextUtils.getApplicationContext());
+            }
+        });
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 0975817..11df2137 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -159,13 +159,14 @@
                         -> assistantCoordinator.getModel().getDetailsModel().set(
                                 AssistantDetailsModel.DETAILS,
                                 new AssistantDetails(movieTitle, /* imageUrl = */ "",
+                                        /* showImage = */ false,
                                         /* totalPriceLabel = */ "",
                                         /* totalPrice = */ "", Calendar.getInstance().getTime(),
                                         descriptionLine1, descriptionLine2,
                                         /* userApprovalRequired= */ false,
                                         /* highlightTitle= */ false, /* highlightLine1= */
                                         false, /* highlightLine1 = */ false,
-                                        /* showPlaceholdersForEmptyFields= */ false)));
+                                        /* animatePlaceholders= */ false)));
         TextView detailsTitle = bottomSheet.findViewById(R.id.details_title);
         TextView detailsLine1 = bottomSheet.findViewById(R.id.details_line1);
         TextView detailsLine2 = bottomSheet.findViewById(R.id.details_line2);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 715cce3..34e3132 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1826,6 +1826,7 @@
     "//chrome/browser/push_messaging:budget_proto",
     "//chrome/browser/resource_coordinator:mojo_bindings",
     "//chrome/browser/safe_browsing",
+    "//chrome/browser/security_events",
     "//chrome/browser/ssl:proto",
     "//chrome/browser/ui",
     "//chrome/browser/ui/webui/bluetooth_internals",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index eb4a547..e6eea37 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3661,11 +3661,6 @@
      flag_descriptions::kLookalikeUrlNavigationSuggestionsDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kLookalikeUrlNavigationSuggestionsUI)},
 
-    {"sync-standalone-transport",
-     flag_descriptions::kSyncStandaloneTransportName,
-     flag_descriptions::kSyncStandaloneTransportDescription, kOsAll,
-     FEATURE_VALUE_TYPE(switches::kSyncStandaloneTransport)},
-
     {"sync-USS-autofill-profile",
      flag_descriptions::kSyncUSSAutofillProfileName,
      flag_descriptions::kSyncUSSAutofillProfileDescription, kOsAll,
@@ -3676,6 +3671,11 @@
      flag_descriptions::kSyncUSSAutofillWalletDataDescription, kOsAll,
      FEATURE_VALUE_TYPE(switches::kSyncUSSAutofillWalletData)},
 
+    {"sync-USS-autofill-wallet-metadata",
+     flag_descriptions::kSyncUSSAutofillWalletMetadataName,
+     flag_descriptions::kSyncUSSAutofillWalletMetadataDescription, kOsAll,
+     FEATURE_VALUE_TYPE(switches::kSyncUSSAutofillWalletMetadata)},
+
     {"enable-resampling-input-events",
      flag_descriptions::kEnableResamplingInputEventsName,
      flag_descriptions::kEnableResamplingInputEventsDescription, kOsAll,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index e7938f4..65c52b1 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -562,9 +562,11 @@
 
   const DetailsProto& proto = details->proto().details();
   const DetailsChangesProto& changes = details->proto().change_flags();
+
   auto jdetails = Java_AssistantDetails_create(
       env, base::android::ConvertUTF8ToJavaString(env, proto.title()),
       base::android::ConvertUTF8ToJavaString(env, proto.image_url()),
+      proto.show_image_placeholder(),
       base::android::ConvertUTF8ToJavaString(env, proto.total_price_label()),
       base::android::ConvertUTF8ToJavaString(env, proto.total_price()),
       base::android::ConvertUTF8ToJavaString(env, details->GetDatetime()),
@@ -574,7 +576,8 @@
       base::android::ConvertUTF8ToJavaString(env, proto.description_line_1()),
       base::android::ConvertUTF8ToJavaString(env, proto.description_line_2()),
       changes.user_approval_required(), changes.highlight_title(),
-      changes.highlight_line1(), changes.highlight_line2());
+      changes.highlight_line1(), changes.highlight_line2(),
+      proto.animate_placeholders());
   Java_AssistantDetailsModel_setDetails(env, jmodel, jdetails);
 }
 
diff --git a/chrome/browser/android/cached_image_fetcher/cached_image_fetcher_bridge.cc b/chrome/browser/android/cached_image_fetcher/cached_image_fetcher_bridge.cc
index 678e4947..0737c2b4 100644
--- a/chrome/browser/android/cached_image_fetcher/cached_image_fetcher_bridge.cc
+++ b/chrome/browser/android/cached_image_fetcher/cached_image_fetcher_bridge.cc
@@ -65,12 +65,14 @@
     JNIEnv* j_env,
     const JavaParamRef<jobject>& j_profile) {
   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
+  SimpleFactoryKey* simple_factory_key = Profile::GetSimpleFactoryKey(profile);
   base::FilePath file_path =
-      CachedImageFetcherServiceFactory::GetCachePath(profile).Append(
-          kPathPostfix);
+      CachedImageFetcherServiceFactory::GetCachePath(simple_factory_key)
+          .Append(kPathPostfix);
 
   CachedImageFetcherService* cif_service =
-      CachedImageFetcherServiceFactory::GetForBrowserContext(profile);
+      CachedImageFetcherServiceFactory::GetForKey(simple_factory_key,
+                                                  profile->GetPrefs());
   CachedImageFetcherBridge* native_cif_bridge = new CachedImageFetcherBridge(
       cif_service->GetCachedImageFetcher(), file_path);
   return reinterpret_cast<intptr_t>(native_cif_bridge);
diff --git a/chrome/browser/android/signin/account_management_screen_helper.cc b/chrome/browser/android/signin/account_management_screen_helper.cc
index 205b847..834c699 100644
--- a/chrome/browser/android/signin/account_management_screen_helper.cc
+++ b/chrome/browser/android/signin/account_management_screen_helper.cc
@@ -9,19 +9,17 @@
 #include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "jni/AccountManagementScreenHelper_jni.h"
+#include "ui/android/window_android.h"
 
 using base::android::JavaParamRef;
 
 // static
 void AccountManagementScreenHelper::OpenAccountManagementScreen(
-    Profile* profile,
+    ui::WindowAndroid* window,
     signin::GAIAServiceType service_type) {
-  DCHECK(profile);
-  DCHECK(ProfileAndroid::FromProfile(profile));
-
+  DCHECK(window);
   Java_AccountManagementScreenHelper_openAccountManagementScreen(
-      base::android::AttachCurrentThread(),
-      ProfileAndroid::FromProfile(profile)->GetJavaObject(),
+      base::android::AttachCurrentThread(), window->GetJavaObject(),
       static_cast<int>(service_type));
 }
 
diff --git a/chrome/browser/android/signin/account_management_screen_helper.h b/chrome/browser/android/signin/account_management_screen_helper.h
index a5a9adf..909ae88 100644
--- a/chrome/browser/android/signin/account_management_screen_helper.h
+++ b/chrome/browser/android/signin/account_management_screen_helper.h
@@ -8,13 +8,15 @@
 #include "base/macros.h"
 #include "components/signin/core/browser/signin_header_helper.h"
 
-class Profile;
+namespace ui {
+class WindowAndroid;
+}
 
 // The glue for Java-side implementation of AccountManagementScreenHelper.
 class AccountManagementScreenHelper {
  public:
   // Opens the account management screen.
-  static void OpenAccountManagementScreen(Profile* profile,
+  static void OpenAccountManagementScreen(ui::WindowAndroid* profile,
                                           signin::GAIAServiceType service_type);
 
  private:
diff --git a/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc
index fff90ec..eeda470 100644
--- a/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc
+++ b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.cc
@@ -13,16 +13,16 @@
 #include "base/task/post_task.h"
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
-#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/search/suggestions/image_decoder_impl.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "components/image_fetcher/core/cache/image_cache.h"
 #include "components/image_fetcher/core/cache/image_data_store_disk.h"
 #include "components/image_fetcher/core/cache/image_metadata_store_leveldb.h"
 #include "components/image_fetcher/core/cached_image_fetcher_service.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
+#include "components/keyed_service/core/simple_dependency_manager.h"
+#include "components/keyed_service/core/simple_factory_key.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace image_fetcher {
 
@@ -37,18 +37,18 @@
 
 // static
 base::FilePath CachedImageFetcherServiceFactory::GetCachePath(
-    Profile* profile) {
+    SimpleFactoryKey* key) {
   base::FilePath cache_path;
-  chrome::GetUserCacheDirectory(profile->GetPath(), &cache_path);
+  chrome::GetUserCacheDirectory(key->path(), &cache_path);
   return cache_path.Append(kImageCacheSubdir);
 }
 
 // static
-CachedImageFetcherService*
-CachedImageFetcherServiceFactory::GetForBrowserContext(
-    content::BrowserContext* context) {
+CachedImageFetcherService* CachedImageFetcherServiceFactory::GetForKey(
+    SimpleFactoryKey* key,
+    PrefService* prefs) {
   return static_cast<CachedImageFetcherService*>(
-      GetInstance()->GetServiceForBrowserContext(context, true));
+      GetInstance()->GetServiceForKey(key, prefs, true));
 }
 
 // static
@@ -58,16 +58,16 @@
 }
 
 CachedImageFetcherServiceFactory::CachedImageFetcherServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-          "CachedImageFetcherService",
-          BrowserContextDependencyManager::GetInstance()) {}
+    : SimpleKeyedServiceFactory("CachedImageFetcherService",
+                                SimpleDependencyManager::GetInstance()) {}
 
 CachedImageFetcherServiceFactory::~CachedImageFetcherServiceFactory() = default;
 
-KeyedService* CachedImageFetcherServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = Profile::FromBrowserContext(context);
-  base::FilePath cache_path = GetCachePath(profile);
+std::unique_ptr<KeyedService>
+CachedImageFetcherServiceFactory::BuildServiceInstanceFor(
+    SimpleFactoryKey* key,
+    PrefService* prefs) const {
+  base::FilePath cache_path = GetCachePath(key);
 
   scoped_refptr<base::SequencedTaskRunner> task_runner =
       base::CreateSequencedTaskRunnerWithTraits(
@@ -80,24 +80,23 @@
       std::make_unique<ImageDataStoreDisk>(cache_path, task_runner);
 
   scoped_refptr<ImageCache> image_cache = base::MakeRefCounted<ImageCache>(
-      std::move(data_store), std::move(metadata_store), profile->GetPrefs(),
-      clock, task_runner);
+      std::move(data_store), std::move(metadata_store), prefs, clock,
+      task_runner);
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
-      content::BrowserContext::GetDefaultStoragePartition(profile)
-          ->GetURLLoaderFactoryForBrowserProcess();
+      SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory();
 
-  return new CachedImageFetcherService(
-      std::make_unique<suggestions::ImageDecoderImpl>(),
-      std::move(url_loader_factory), std::move(image_cache),
-      context->IsOffTheRecord());
+  auto cached_image_fetcher_service =
+      std::make_unique<CachedImageFetcherService>(
+          std::make_unique<suggestions::ImageDecoderImpl>(),
+          std::move(url_loader_factory), std::move(image_cache),
+          key->is_off_the_record());
+  return cached_image_fetcher_service;
 }
 
-content::BrowserContext*
-CachedImageFetcherServiceFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  // Return different BrowserContexts for regular/incognito.
-  return context;
+SimpleFactoryKey* CachedImageFetcherServiceFactory::GetKeyToUse(
+    SimpleFactoryKey* key) const {
+  return key;
 }
 
 }  // namespace image_fetcher
diff --git a/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h
index 0c68a082..07177143 100644
--- a/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h
+++ b/chrome/browser/cached_image_fetcher/cached_image_fetcher_service_factory.h
@@ -5,30 +5,28 @@
 #ifndef CHROME_BROWSER_CACHED_IMAGE_FETCHER_CACHED_IMAGE_FETCHER_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_CACHED_IMAGE_FETCHER_CACHED_IMAGE_FETCHER_SERVICE_FACTORY_H_
 
+#include <memory>
+
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/simple_keyed_service_factory.h"
 
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-class Profile;
+class SimpleFactoryKey;
+class PrefService;
 
 namespace image_fetcher {
 
 class CachedImageFetcherService;
 
 // Factory to create one CachedImageFetcherService per browser context.
-class CachedImageFetcherServiceFactory
-    : public BrowserContextKeyedServiceFactory {
+class CachedImageFetcherServiceFactory : public SimpleKeyedServiceFactory {
  public:
   // Return the cache path for the given profile.
-  static base::FilePath GetCachePath(Profile* profile);
+  static base::FilePath GetCachePath(SimpleFactoryKey* key);
 
-  static CachedImageFetcherService* GetForBrowserContext(
-      content::BrowserContext* context);
+  static CachedImageFetcherService* GetForKey(SimpleFactoryKey* key,
+                                              PrefService* prefs);
   static CachedImageFetcherServiceFactory* GetInstance();
 
  private:
@@ -37,11 +35,11 @@
   CachedImageFetcherServiceFactory();
   ~CachedImageFetcherServiceFactory() override;
 
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
+  // SimpleKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      SimpleFactoryKey* key,
+      PrefService* prefs) const override;
+  SimpleFactoryKey* GetKeyToUse(SimpleFactoryKey* key) const override;
 
   DISALLOW_COPY_AND_ASSIGN(CachedImageFetcherServiceFactory);
 };
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 2ac704f..8bba8b3 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -13,6 +13,7 @@
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
 #include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
 #include "chrome/browser/metrics/persistent_histograms.h"
@@ -23,6 +24,8 @@
 #include "components/version_info/version_info.h"
 
 #if defined(OS_ANDROID)
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/android/reached_code_profiler.h"
 #include "chrome/browser/chrome_browser_field_trials_mobile.h"
 #else
 #include "chrome/browser/chrome_browser_field_trials_desktop.h"
@@ -85,6 +88,33 @@
   }
 }
 
+void ChromeBrowserFieldTrials::RegisterSyntheticTrials() {
+#if defined(OS_ANDROID)
+  static constexpr char kEnabledGroup[] = "Enabled";
+  static constexpr char kDisabledGroup[] = "Disabled";
+
+  static constexpr char kOrderfileOptimizationTrial[] =
+      "AndroidOrderfileOptimization";
+  if (base::android::IsUsingOrderfileOptimization()) {
+    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        kOrderfileOptimizationTrial, kEnabledGroup);
+  } else {
+    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        kOrderfileOptimizationTrial, kDisabledGroup);
+  }
+
+  static constexpr char kReachedCodeProfilerTrial[] =
+      "ReachedCodeProfilerSynthetic";
+  if (base::android::IsReachedCodeProfilerEnabled()) {
+    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        kReachedCodeProfilerTrial, kEnabledGroup);
+  } else {
+    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        kReachedCodeProfilerTrial, kDisabledGroup);
+  }
+#endif  // defined(OS_ANDROID)
+}
+
 void ChromeBrowserFieldTrials::InstantiateDynamicTrials() {
   // Persistent histograms must be enabled as soon as possible.
   InstantiatePersistentHistograms();
diff --git a/chrome/browser/chrome_browser_field_trials.h b/chrome/browser/chrome_browser_field_trials.h
index 747a8a7..e80020b 100644
--- a/chrome/browser/chrome_browser_field_trials.h
+++ b/chrome/browser/chrome_browser_field_trials.h
@@ -22,6 +22,7 @@
   void SetupFeatureControllingFieldTrials(
       bool has_seed,
       base::FeatureList* feature_list) override;
+  void RegisterSyntheticTrials() override;
 
  private:
   // Instantiates dynamic trials by querying their state, to ensure they get
diff --git a/chrome/browser/chrome_browser_field_trials_mobile.cc b/chrome/browser/chrome_browser_field_trials_mobile.cc
index e999f030..fba54a6 100644
--- a/chrome/browser/chrome_browser_field_trials_mobile.cc
+++ b/chrome/browser/chrome_browser_field_trials_mobile.cc
@@ -5,48 +5,18 @@
 #include "chrome/browser/chrome_browser_field_trials_mobile.h"
 
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 
 #if defined(OS_ANDROID)
-#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/android/reached_code_profiler.h"
-#include "base/base_switches.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/prerender/prerender_field_trial.h"
 #endif
 
 namespace chrome {
 
 void SetupMobileFieldTrials() {
+  DCHECK(!g_browser_process);
 #if defined(OS_ANDROID)
   prerender::ConfigureNoStatePrefetch();
-
-  // For tests on some platforms, g_browser_process is not initialized yet.
-  if (g_browser_process) {
-    static constexpr char kEnabledGroup[] = "Enabled";
-    static constexpr char kDisabledGroup[] = "Disabled";
-
-    static constexpr char kOrderfileOptimizationTrial[] =
-        "AndroidOrderfileOptimization";
-    if (base::android::IsUsingOrderfileOptimization()) {
-      ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-          kOrderfileOptimizationTrial, kEnabledGroup);
-    } else {
-      ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-          kOrderfileOptimizationTrial, kDisabledGroup);
-    }
-
-    static constexpr char kReachedCodeProfilerTrial[] =
-        "ReachedCodeProfilerSynthetic";
-    if (base::android::IsReachedCodeProfilerEnabled()) {
-      ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-          kReachedCodeProfilerTrial, kEnabledGroup);
-    } else {
-      ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-          kReachedCodeProfilerTrial, kDisabledGroup);
-    }
-  }
-
 #endif
 }
 
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 657dff3..0b78469 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -661,6 +661,9 @@
       variations::SyntheticTrialsActiveGroupIdProvider::GetInstance());
   // Now that field trials have been created, initializes metrics recording.
   metrics->InitializeMetricsRecordingState();
+
+  chrome_feature_list_creator_->browser_field_trials()
+      ->RegisterSyntheticTrials();
 }
 
 void ChromeBrowserMainParts::StartMetricsRecording() {
diff --git a/chrome/browser/chrome_browser_main.h b/chrome/browser/chrome_browser_main.h
index ae86339..679f397 100644
--- a/chrome/browser/chrome_browser_main.h
+++ b/chrome/browser/chrome_browser_main.h
@@ -10,7 +10,6 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_browser_field_trials.h"
 #include "chrome/browser/chrome_process_singleton.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/process_singleton.h"
@@ -138,8 +137,6 @@
   const base::CommandLine& parsed_command_line_;
   int result_code_;
 
-  ChromeBrowserFieldTrials browser_field_trials_;
-
 #if !defined(OS_ANDROID)
   // Create StartupTimeBomb object for watching jank during startup.
   std::unique_ptr<StartupTimeBomb> startup_watcher_;
diff --git a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
index 3626926..0a7678a 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
@@ -114,14 +114,6 @@
       return;
     }
 
-    // |IdentityManager::LegacySeedAccountInfo| must be called before
-    // |AccountManager::UpsertToken|. |AccountManager| observers will need to
-    // translate |AccountManager::AccountKey| to other formats using
-    // |IdentityManager| and hence |IdentityManager| should be updated first.
-    AccountInfo account_info;
-    account_info.email = email;
-    account_info.gaia = gaia_id;
-    identity_manager_->LegacySeedAccountInfo(account_info);
     account_manager_->UpsertAccount(
         AccountManager::AccountKey{
             gaia_id, account_manager::AccountType::ACCOUNT_TYPE_GAIA},
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 8e8a98a..2edcaf5 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -578,6 +578,11 @@
         TestCase("dirCreateWithoutChangingCurrent").EnableMyFilesVolume(),
         TestCase("dirCreateWithoutChangingCurrent"),
         TestCase("dirContextMenuRecent"),
+        TestCase("dirContextMenuMyFiles").EnableMyFilesVolume(),
+        TestCase("dirContextMenuCrostini"),
+        TestCase("dirContextMenuPlayFiles"),
+        TestCase("dirContextMenuUsbs"),
+        TestCase("dirContextMenuFsp"),
         TestCase("dirContextMenuShortcut")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/chromeos/oauth2_token_service_delegate.cc b/chrome/browser/chromeos/oauth2_token_service_delegate.cc
index 61512d7..d7b3e02 100644
--- a/chrome/browser/chromeos/oauth2_token_service_delegate.cc
+++ b/chrome/browser/chromeos/oauth2_token_service_delegate.cc
@@ -314,7 +314,7 @@
     account_manager_->UpdateEmail(account.key, email);
   }
 
-  std::string account_id = account_tracker_service_->PickAccountIdForAccount(
+  std::string account_id = account_tracker_service_->SeedAccountInfo(
       account.key.id /* gaia_id */, email);
   DCHECK(!account_id.empty());
 
diff --git a/chrome/browser/chromeos/policy/display_resolution_handler.cc b/chrome/browser/chromeos/policy/display_resolution_handler.cc
index 4d83d00..feb7d5e 100644
--- a/chrome/browser/chromeos/policy/display_resolution_handler.cc
+++ b/chrome/browser/chromeos/policy/display_resolution_handler.cc
@@ -183,11 +183,13 @@
   // We should reset locally stored settings and clear list of already updated
   // displays if any of the policy values were updated.
   bool should_reset_settings = false;
-  should_reset_settings |= !new_external_config ||
-                           !external_display_settings_ ||
+  should_reset_settings |=
+      bool{new_external_config} != bool{external_display_settings_};
+  should_reset_settings |= new_external_config && external_display_settings_ &&
                            *new_external_config != *external_display_settings_;
-  should_reset_settings |= !new_internal_config ||
-                           !internal_display_settings_ ||
+  should_reset_settings |=
+      bool{new_internal_config} != bool{internal_display_settings_};
+  should_reset_settings |= new_internal_config && internal_display_settings_ &&
                            *new_internal_config != *internal_display_settings_;
   should_reset_settings |= recommended_ != new_recommended;
 
diff --git a/chrome/browser/extensions/api/resources_private/resources_private_api.cc b/chrome/browser/extensions/api/resources_private/resources_private_api.cc
index ef039e9..cc6e3d5 100644
--- a/chrome/browser/extensions/api/resources_private/resources_private_api.cc
+++ b/chrome/browser/extensions/api/resources_private/resources_private_api.cc
@@ -9,6 +9,7 @@
 
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/webui/localized_string.h"
 #include "chrome/common/extensions/api/resources_private.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
@@ -30,104 +31,85 @@
 
 namespace {
 
-void SetL10nString(base::DictionaryValue* dict, const std::string& string_id,
-                   int resource_id) {
-  dict->SetString(string_id, l10n_util::GetStringUTF16(resource_id));
-}
-
 void AddStringsForIdentity(base::DictionaryValue* dict) {
-  SetL10nString(dict, "window-title", IDS_EXTENSION_CONFIRM_PERMISSIONS);
+  dict->SetString("window-title",
+                  l10n_util::GetStringUTF16(IDS_EXTENSION_CONFIRM_PERMISSIONS));
 }
 
 void AddStringsForPdf(base::DictionaryValue* dict) {
-  SetL10nString(dict, "passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE);
-  SetL10nString(dict, "passwordPrompt", IDS_PDF_NEED_PASSWORD);
-  SetL10nString(dict, "passwordSubmit", IDS_PDF_PASSWORD_SUBMIT);
-  SetL10nString(dict, "passwordInvalid", IDS_PDF_PASSWORD_INVALID);
-  SetL10nString(dict, "pageLoading", IDS_PDF_PAGE_LOADING);
-  SetL10nString(dict, "pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED);
-  SetL10nString(dict, "errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE);
-  SetL10nString(dict, "pageReload", IDS_PDF_PAGE_RELOAD_BUTTON);
-  SetL10nString(dict, "bookmarks", IDS_PDF_BOOKMARKS);
-  SetL10nString(dict, "labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER);
-  SetL10nString(dict, "tooltipRotateCW", IDS_PDF_TOOLTIP_ROTATE_CW);
-  SetL10nString(dict, "tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD);
-  SetL10nString(dict, "tooltipPrint", IDS_PDF_TOOLTIP_PRINT);
-  SetL10nString(dict, "tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE);
-  SetL10nString(dict, "tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH);
-  SetL10nString(dict, "tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN);
-  SetL10nString(dict, "tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT);
+  static constexpr LocalizedString kPdfResources[] = {
+    {"passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE},
+    {"passwordPrompt", IDS_PDF_NEED_PASSWORD},
+    {"passwordSubmit", IDS_PDF_PASSWORD_SUBMIT},
+    {"passwordInvalid", IDS_PDF_PASSWORD_INVALID},
+    {"pageLoading", IDS_PDF_PAGE_LOADING},
+    {"pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED},
+    {"errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE},
+    {"pageReload", IDS_PDF_PAGE_RELOAD_BUTTON},
+    {"bookmarks", IDS_PDF_BOOKMARKS},
+    {"labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER},
+    {"tooltipRotateCW", IDS_PDF_TOOLTIP_ROTATE_CW},
+    {"tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD},
+    {"tooltipPrint", IDS_PDF_TOOLTIP_PRINT},
+    {"tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE},
+    {"tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH},
+    {"tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN},
+    {"tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT},
 #if defined(OS_CHROMEOS)
-  SetL10nString(dict, "tooltipAnnotate", IDS_PDF_ANNOTATION_ANNOTATE);
-  SetL10nString(dict, "annotationDocumentTooLarge",
-                IDS_PDF_ANNOTATION_DOCUMENT_TOO_LARGE);
-  SetL10nString(dict, "annotationDocumentProtected",
-                IDS_PDF_ANNOTATION_DOCUMENT_PROTECTED);
-  SetL10nString(dict, "annotationDocumentRotated",
-                IDS_PDF_ANNOTATION_DOCUMENT_ROTATED);
-  SetL10nString(dict, "annotationPen", IDS_PDF_ANNOTATION_PEN);
-  SetL10nString(dict, "annotationHighlighter", IDS_PDF_ANNOTATION_HIGHLIGHTER);
-  SetL10nString(dict, "annotationEraser", IDS_PDF_ANNOTATION_ERASER);
-  SetL10nString(dict, "annotationUndo", IDS_PDF_ANNOTATION_UNDO);
-  SetL10nString(dict, "annotationRedo", IDS_PDF_ANNOTATION_REDO);
-  SetL10nString(dict, "annotationExpand", IDS_PDF_ANNOTATION_EXPAND);
-  SetL10nString(dict, "annotationColorBlack", IDS_PDF_ANNOTATION_COLOR_BLACK);
-  SetL10nString(dict, "annotationColorRed", IDS_PDF_ANNOTATION_COLOR_RED);
-  SetL10nString(dict, "annotationColorYellow", IDS_PDF_ANNOTATION_COLOR_YELLOW);
-  SetL10nString(dict, "annotationColorGreen", IDS_PDF_ANNOTATION_COLOR_GREEN);
-  SetL10nString(dict, "annotationColorCyan", IDS_PDF_ANNOTATION_COLOR_CYAN);
-  SetL10nString(dict, "annotationColorPurple", IDS_PDF_ANNOTATION_COLOR_PURPLE);
-  SetL10nString(dict, "annotationColorBrown", IDS_PDF_ANNOTATION_COLOR_BROWN);
-  SetL10nString(dict, "annotationColorWhite", IDS_PDF_ANNOTATION_COLOR_WHITE);
-  SetL10nString(dict, "annotationColorCrimson",
-                IDS_PDF_ANNOTATION_COLOR_CRIMSON);
-  SetL10nString(dict, "annotationColorAmber", IDS_PDF_ANNOTATION_COLOR_AMBER);
-  SetL10nString(dict, "annotationColorAvocadoGreen",
-                IDS_PDF_ANNOTATION_COLOR_AVOCADO_GREEN);
-  SetL10nString(dict, "annotationColorCobaltBlue",
-                IDS_PDF_ANNOTATION_COLOR_COBALT_BLUE);
-  SetL10nString(dict, "annotationColorDeepPurple",
-                IDS_PDF_ANNOTATION_COLOR_DEEP_PURPLE);
-  SetL10nString(dict, "annotationColorDarkBrown",
-                IDS_PDF_ANNOTATION_COLOR_DARK_BROWN);
-  SetL10nString(dict, "annotationColorDarkGrey",
-                IDS_PDF_ANNOTATION_COLOR_DARK_GREY);
-  SetL10nString(dict, "annotationColorHotPink",
-                IDS_PDF_ANNOTATION_COLOR_HOT_PINK);
-  SetL10nString(dict, "annotationColorOrange", IDS_PDF_ANNOTATION_COLOR_ORANGE);
-  SetL10nString(dict, "annotationColorLime", IDS_PDF_ANNOTATION_COLOR_LIME);
-  SetL10nString(dict, "annotationColorBlue", IDS_PDF_ANNOTATION_COLOR_BLUE);
-  SetL10nString(dict, "annotationColorViolet", IDS_PDF_ANNOTATION_COLOR_VIOLET);
-  SetL10nString(dict, "annotationColorTeal", IDS_PDF_ANNOTATION_COLOR_TEAL);
-  SetL10nString(dict, "annotationColorLightGrey",
-                IDS_PDF_ANNOTATION_COLOR_LIGHT_GREY);
-  SetL10nString(dict, "annotationColorLightPink",
-                IDS_PDF_ANNOTATION_COLOR_LIGHT_PINK);
-  SetL10nString(dict, "annotationColorLightOrange",
-                IDS_PDF_ANNOTATION_COLOR_LIGHT_ORANGE);
-  SetL10nString(dict, "annotationColorLightGreen",
-                IDS_PDF_ANNOTATION_COLOR_LIGHT_GREEN);
-  SetL10nString(dict, "annotationColorLightBlue",
-                IDS_PDF_ANNOTATION_COLOR_LIGHT_BLUE);
-  SetL10nString(dict, "annotationColorLavender",
-                IDS_PDF_ANNOTATION_COLOR_LAVENDER);
-  SetL10nString(dict, "annotationColorLightTeal",
-                IDS_PDF_ANNOTATION_COLOR_LIGHT_TEAL);
-  SetL10nString(dict, "annotationSize1", IDS_PDF_ANNOTATION_SIZE1);
-  SetL10nString(dict, "annotationSize2", IDS_PDF_ANNOTATION_SIZE2);
-  SetL10nString(dict, "annotationSize3", IDS_PDF_ANNOTATION_SIZE3);
-  SetL10nString(dict, "annotationSize4", IDS_PDF_ANNOTATION_SIZE4);
-  SetL10nString(dict, "annotationSize8", IDS_PDF_ANNOTATION_SIZE8);
-  SetL10nString(dict, "annotationSize12", IDS_PDF_ANNOTATION_SIZE12);
-  SetL10nString(dict, "annotationSize16", IDS_PDF_ANNOTATION_SIZE16);
-  SetL10nString(dict, "annotationSize20", IDS_PDF_ANNOTATION_SIZE20);
-  SetL10nString(dict, "annotationFormWarningTitle",
-                IDS_PDF_DISCARD_FORM_CHANGES);
-  SetL10nString(dict, "annotationFormWarningDetail",
-                IDS_PDF_DISCARD_FORM_CHANGES_DETAIL);
-  SetL10nString(dict, "annotationFormWarningKeepEditing", IDS_PDF_KEEP_EDITING);
-  SetL10nString(dict, "annotationFormWarningDiscard", IDS_PDF_DISCARD);
-#endif
+    {"tooltipAnnotate", IDS_PDF_ANNOTATION_ANNOTATE},
+    {"annotationDocumentTooLarge", IDS_PDF_ANNOTATION_DOCUMENT_TOO_LARGE},
+    {"annotationDocumentProtected", IDS_PDF_ANNOTATION_DOCUMENT_PROTECTED},
+    {"annotationDocumentRotated", IDS_PDF_ANNOTATION_DOCUMENT_ROTATED},
+    {"annotationPen", IDS_PDF_ANNOTATION_PEN},
+    {"annotationHighlighter", IDS_PDF_ANNOTATION_HIGHLIGHTER},
+    {"annotationEraser", IDS_PDF_ANNOTATION_ERASER},
+    {"annotationUndo", IDS_PDF_ANNOTATION_UNDO},
+    {"annotationRedo", IDS_PDF_ANNOTATION_REDO},
+    {"annotationExpand", IDS_PDF_ANNOTATION_EXPAND},
+    {"annotationColorBlack", IDS_PDF_ANNOTATION_COLOR_BLACK},
+    {"annotationColorRed", IDS_PDF_ANNOTATION_COLOR_RED},
+    {"annotationColorYellow", IDS_PDF_ANNOTATION_COLOR_YELLOW},
+    {"annotationColorGreen", IDS_PDF_ANNOTATION_COLOR_GREEN},
+    {"annotationColorCyan", IDS_PDF_ANNOTATION_COLOR_CYAN},
+    {"annotationColorPurple", IDS_PDF_ANNOTATION_COLOR_PURPLE},
+    {"annotationColorBrown", IDS_PDF_ANNOTATION_COLOR_BROWN},
+    {"annotationColorWhite", IDS_PDF_ANNOTATION_COLOR_WHITE},
+    {"annotationColorCrimson", IDS_PDF_ANNOTATION_COLOR_CRIMSON},
+    {"annotationColorAmber", IDS_PDF_ANNOTATION_COLOR_AMBER},
+    {"annotationColorAvocadoGreen", IDS_PDF_ANNOTATION_COLOR_AVOCADO_GREEN},
+    {"annotationColorCobaltBlue", IDS_PDF_ANNOTATION_COLOR_COBALT_BLUE},
+    {"annotationColorDeepPurple", IDS_PDF_ANNOTATION_COLOR_DEEP_PURPLE},
+    {"annotationColorDarkBrown", IDS_PDF_ANNOTATION_COLOR_DARK_BROWN},
+    {"annotationColorDarkGrey", IDS_PDF_ANNOTATION_COLOR_DARK_GREY},
+    {"annotationColorHotPink", IDS_PDF_ANNOTATION_COLOR_HOT_PINK},
+    {"annotationColorOrange", IDS_PDF_ANNOTATION_COLOR_ORANGE},
+    {"annotationColorLime", IDS_PDF_ANNOTATION_COLOR_LIME},
+    {"annotationColorBlue", IDS_PDF_ANNOTATION_COLOR_BLUE},
+    {"annotationColorViolet", IDS_PDF_ANNOTATION_COLOR_VIOLET},
+    {"annotationColorTeal", IDS_PDF_ANNOTATION_COLOR_TEAL},
+    {"annotationColorLightGrey", IDS_PDF_ANNOTATION_COLOR_LIGHT_GREY},
+    {"annotationColorLightPink", IDS_PDF_ANNOTATION_COLOR_LIGHT_PINK},
+    {"annotationColorLightOrange", IDS_PDF_ANNOTATION_COLOR_LIGHT_ORANGE},
+    {"annotationColorLightGreen", IDS_PDF_ANNOTATION_COLOR_LIGHT_GREEN},
+    {"annotationColorLightBlue", IDS_PDF_ANNOTATION_COLOR_LIGHT_BLUE},
+    {"annotationColorLavender", IDS_PDF_ANNOTATION_COLOR_LAVENDER},
+    {"annotationColorLightTeal", IDS_PDF_ANNOTATION_COLOR_LIGHT_TEAL},
+    {"annotationSize1", IDS_PDF_ANNOTATION_SIZE1},
+    {"annotationSize2", IDS_PDF_ANNOTATION_SIZE2},
+    {"annotationSize3", IDS_PDF_ANNOTATION_SIZE3},
+    {"annotationSize4", IDS_PDF_ANNOTATION_SIZE4},
+    {"annotationSize8", IDS_PDF_ANNOTATION_SIZE8},
+    {"annotationSize12", IDS_PDF_ANNOTATION_SIZE12},
+    {"annotationSize16", IDS_PDF_ANNOTATION_SIZE16},
+    {"annotationSize20", IDS_PDF_ANNOTATION_SIZE20},
+    {"annotationFormWarningTitle", IDS_PDF_DISCARD_FORM_CHANGES},
+    {"annotationFormWarningDetail", IDS_PDF_DISCARD_FORM_CHANGES_DETAIL},
+    {"annotationFormWarningKeepEditing", IDS_PDF_KEEP_EDITING},
+    {"annotationFormWarningDiscard", IDS_PDF_DISCARD},
+#endif  // defined(OS_CHROMEOS)
+  };
+  for (const auto& resource : kPdfResources)
+    dict->SetString(resource.name, l10n_util::GetStringUTF16(resource.id));
 }
 
 void AddAdditionalDataForPdf(base::DictionaryValue* dict) {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index d2967ab..178fc9bd 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1927,8 +1927,8 @@
   },
   {
     "name": "enable-webnfc",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "rijubrata.bhaumik@intel.com","reillyg" ],
+    "expiry_milestone": 79
   },
   {
     "name": "enable-webrtc-h264-with-openh264-ffmpeg",
@@ -2935,6 +2935,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "sync-USS-autofill-wallet-metadata",
+    "owners": [ "jkrcal", "//components/sync/OWNERS" ],
+    "expiry_milestone": 76
+  },
+  {
     "name": "sync-standalone-transport",
     "owners": [ "treib", "//components/sync/OWNERS" ],
     "expiry_milestone": 76
@@ -3168,8 +3173,8 @@
   },
   {
     "name": "webrtc-unified-plan-by-default",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "//third_party/blink/renderer/modules/peerconnection/OWNERS", "hbos@chromium.org" ],
+    "expiry_milestone": 75
   },
   {
     "name": "webxr",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index ac93910..c23f9017 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1825,12 +1825,6 @@
     "Match Autofill suggestions based on substrings (token prefixes) rather "
     "than just prefixes.";
 
-const char kSyncStandaloneTransportName[] = "Allow Sync standalone transport";
-const char kSyncStandaloneTransportDescription[] =
-    "If enabled, allows Chrome Sync to start in standalone transport mode. In "
-    "this mode, the Sync machinery can start without user opt-in, but only a "
-    "subset of data types are supported.";
-
 const char kSyncSupportSecondaryAccountName[] =
     "Support secondary accounts for Sync standalone transport";
 const char kSyncSupportSecondaryAccountDescription[] =
@@ -1847,6 +1841,11 @@
 const char kSyncUSSAutofillWalletDataDescription[] =
     "Enables the new implementation of autofill walet data sync";
 
+const char kSyncUSSAutofillWalletMetadataName[] =
+    "Enable USS for autofill wallet metadata";
+const char kSyncUSSAutofillWalletMetadataDescription[] =
+    "Enables the new implementation of autofill walet metadata sync";
+
 const char kSyncSandboxName[] = "Use Chrome Sync sandbox";
 const char kSyncSandboxDescription[] =
     "Connects to the testing server for Chrome Sync.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 93b418ae..1bda146 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1086,9 +1086,6 @@
 extern const char kSyncSandboxName[];
 extern const char kSyncSandboxDescription[];
 
-extern const char kSyncStandaloneTransportName[];
-extern const char kSyncStandaloneTransportDescription[];
-
 extern const char kSyncSupportSecondaryAccountName[];
 extern const char kSyncSupportSecondaryAccountDescription[];
 
@@ -1098,6 +1095,9 @@
 extern const char kSyncUSSAutofillWalletDataName[];
 extern const char kSyncUSSAutofillWalletDataDescription[];
 
+extern const char kSyncUSSAutofillWalletMetadataName[];
+extern const char kSyncUSSAutofillWalletMetadataDescription[];
+
 extern const char kTabGridLayoutAndroidName[];
 extern const char kTabGridLayoutAndroidDescription[];
 
diff --git a/chrome/browser/media/router/discovery/dial/dial_service.cc b/chrome/browser/media/router/discovery/dial/dial_service.cc
index 0c27310a..6df061a 100644
--- a/chrome/browser/media/router/discovery/dial/dial_service.cc
+++ b/chrome/browser/media/router/discovery/dial/dial_service.cc
@@ -321,9 +321,9 @@
 bool DialServiceImpl::DialSocket::ParseResponse(const std::string& response,
                                                 const base::Time& response_time,
                                                 DialDeviceData* device) {
-  int headers_end =
+  size_t headers_end =
       HttpUtil::LocateEndOfHeaders(response.c_str(), response.size());
-  if (headers_end < 1) {
+  if (headers_end == 0 || headers_end == std::string::npos) {
     VLOG(1) << "Headers invalid or empty, ignoring: " << response;
     return false;
   }
diff --git a/chrome/browser/metrics/chrome_feature_list_creator.h b/chrome/browser/metrics/chrome_feature_list_creator.h
index 8e0f2eff1..1b3434c 100644
--- a/chrome/browser/metrics/chrome_feature_list_creator.h
+++ b/chrome/browser/metrics/chrome_feature_list_creator.h
@@ -69,6 +69,10 @@
   }
   const std::string& actual_locale() { return actual_locale_; }
 
+  ChromeBrowserFieldTrials* browser_field_trials() {
+    return browser_field_trials_.get();
+  }
+
  private:
   void CreatePrefService();
   void ConvertFlagsToSwitches();
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h
index 4e08bee..a3f2f59 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.h
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -85,7 +85,7 @@
   friend class ::CrashesDOMHandler;
   friend class ::FlashDOMHandler;
   friend void chrome::AttemptRestart();
-  friend void chrome::SetupMobileFieldTrials();
+  friend class ChromeBrowserFieldTrials;
   // For ChromeWinClang.
   friend class ChromeBrowserMainExtraPartsMetrics;
   // For StackSamplingConfiguration.
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 8707e11..fee542cf 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -332,9 +332,7 @@
 class UkmBrowserTestWithSyncTransport : public UkmBrowserTest {
  public:
   UkmBrowserTestWithSyncTransport() {
-    features_.InitWithFeatures({switches::kSyncStandaloneTransport,
-                                switches::kSyncSupportSecondaryAccount},
-                               {});
+    features_.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
   }
 
   void SetUpInProcessBrowserTestFixture() override {
diff --git a/chrome/browser/offline_pages/android/prefetch_test_bridge.cc b/chrome/browser/offline_pages/android/prefetch_test_bridge.cc
index d65ea68..3900cd1 100644
--- a/chrome/browser/offline_pages/android/prefetch_test_bridge.cc
+++ b/chrome/browser/offline_pages/android/prefetch_test_bridge.cc
@@ -49,9 +49,10 @@
     const JavaParamRef<jbyteArray>& j_image_data) {
   Profile* profile = ProfileManager::GetLastUsedProfile();
   DCHECK(profile);
+  SimpleFactoryKey* simple_factory_key = Profile::GetSimpleFactoryKey(profile);
   image_fetcher::CachedImageFetcherService* service =
-      image_fetcher::CachedImageFetcherServiceFactory::GetForBrowserContext(
-          profile);
+      image_fetcher::CachedImageFetcherServiceFactory::GetForKey(
+          simple_factory_key, profile->GetPrefs());
   DCHECK(service);
   scoped_refptr<image_fetcher::ImageCache> cache =
       service->ImageCacheForTesting();
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
index c254f63e..8c692f4 100644
--- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
+++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -47,7 +47,9 @@
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(DownloadServiceFactory::GetInstance());
   DependsOn(OfflinePageModelFactory::GetInstance());
-  DependsOn(image_fetcher::CachedImageFetcherServiceFactory::GetInstance());
+  // TODO(hanxi): add
+  // DependsOn(image_fetcher::CachedImageFetcherServiceFactory::GetInstance());
+  // when the PrefetchServiceFactory becomes a SimpleKeyedServiceFactory.
 }
 
 // static
@@ -104,9 +106,11 @@
     suggested_articles_observer = std::make_unique<SuggestedArticlesObserver>();
     thumbnail_fetcher = std::make_unique<ThumbnailFetcherImpl>();
   } else {
+    SimpleFactoryKey* simple_factory_key =
+        Profile::GetSimpleFactoryKey(profile);
     image_fetcher::CachedImageFetcherService* image_fetcher_service =
-        image_fetcher::CachedImageFetcherServiceFactory::GetForBrowserContext(
-            context);
+        image_fetcher::CachedImageFetcherServiceFactory::GetForKey(
+            simple_factory_key, profile->GetPrefs());
     DCHECK(image_fetcher_service);
     thumbnail_image_fetcher = image_fetcher_service->GetCachedImageFetcher();
   }
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
index 444bc67..04b79c43 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.cc
@@ -149,6 +149,11 @@
   RegisterInputEventObserver(new_host);
 }
 
+void MetricsWebContentsObserver::FrameDeleted(content::RenderFrameHost* rfh) {
+  if (committed_load_)
+    committed_load_->FrameDeleted(rfh);
+}
+
 void MetricsWebContentsObserver::MediaStartedPlaying(
     const content::WebContentsObserver::MediaPlayerInfo& video_type,
     const content::WebContentsObserver::MediaPlayerId& id) {
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
index 395659f..faee044 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer.h
@@ -105,6 +105,7 @@
   void RenderProcessGone(base::TerminationStatus status) override;
   void RenderViewHostChanged(content::RenderViewHost* old_host,
                              content::RenderViewHost* new_host) override;
+  void FrameDeleted(content::RenderFrameHost* render_frame_host) override;
   void MediaStartedPlaying(
       const content::WebContentsObserver::MediaPlayerInfo& video_type,
       const content::WebContentsObserver::MediaPlayerId& id) override;
diff --git a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc
index 89cc069..2e3ff51e 100644
--- a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h"
 
+#include <algorithm>
 #include <string>
 
 #include "base/optional.h"
@@ -13,6 +14,8 @@
 #include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "components/google/core/common/google_util.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "net/base/url_util.h"
 #include "url/gurl.h"
 
 namespace {
@@ -33,6 +36,25 @@
 const char kHistogramAMPParseStartRedirect[] =
     "ParseTiming.NavigationToParseStart.RedirectToNonAmpPage";
 
+const char kHistogramAMPSubframeNavigationToInput[] =
+    "Experimental.PageTiming.NavigationToInput.Subframe";
+const char kHistogramAMPSubframeInputToNavigation[] =
+    "Experimental.PageTiming.InputToNavigation.Subframe";
+const char kHistogramAMPSubframeMainFrameToSubFrameNavigation[] =
+    "Experimental.PageTiming.MainFrameToSubFrameNavigationDelta.Subframe";
+const char kHistogramAMPSubframeFirstContentfulPaint[] =
+    "PaintTiming.InputToFirstContentfulPaint.Subframe";
+const char kHistogramAMPSubframeFirstContentfulPaintFullNavigation[] =
+    "PaintTiming.InputToFirstContentfulPaint.Subframe.FullNavigation";
+const char kHistogramAMPSubframeLargestContentPaint[] =
+    "PaintTiming.InputToLargestContentPaint.Subframe";
+const char kHistogramAMPSubframeLargestContentPaintFullNavigation[] =
+    "PaintTiming.InputToLargestContentPaint.Subframe.FullNavigation";
+const char kHistogramAMPSubframeFirstInputDelay[] =
+    "InteractiveTiming.FirstInputDelay3.Subframe";
+const char kHistogramAMPSubframeFirstInputDelayFullNavigation[] =
+    "InteractiveTiming.FirstInputDelay3.Subframe.FullNavigation";
+
 // Host pattern for AMP Cache URLs.
 // See https://developers.google.com/amp/cache/overview#amp-cache-url-format
 // for a definition of the format of AMP Cache URLs.
@@ -77,18 +99,44 @@
   return url.ReplaceComponents(replacements);
 }
 
+// Extracts the AMP viewer URL from an AMP cache URL, as encoded in a fragment
+// parameter.
+GURL GetViewerUrlFromCacheUrl(const GURL& url) {
+  // The viewer URL is encoded in the fragment as a query string parameter
+  // (&viewerURL=<URL>). net::QueryIterator only operates on the query string,
+  // so we copy the fragment into the query string, then iterate over the
+  // parameters below.
+  std::string ref = url.ref();
+  GURL::Replacements replacements;
+  replacements.SetQuery(ref.c_str(), url::Component(0, ref.length()));
+  GURL modified_url = url.ReplaceComponents(replacements);
+  for (net::QueryIterator it(modified_url); !it.IsAtEnd(); it.Advance()) {
+    if (it.GetKey() == "viewerUrl")
+      return GURL(it.GetUnescapedValue());
+  }
+  return GURL::EmptyGURL();
+}
+
+base::TimeDelta ClampToZero(base::TimeDelta t) {
+  return std::max(base::TimeDelta(), t);
+}
+
 }  // namespace
 
 AMPPageLoadMetricsObserver::AMPPageLoadMetricsObserver() {}
 
 AMPPageLoadMetricsObserver::~AMPPageLoadMetricsObserver() {}
 
+AMPPageLoadMetricsObserver::SubFrameInfo::SubFrameInfo() = default;
+AMPPageLoadMetricsObserver::SubFrameInfo::~SubFrameInfo() = default;
+
 page_load_metrics::PageLoadMetricsObserver::ObservePolicy
 AMPPageLoadMetricsObserver::OnCommit(
     content::NavigationHandle* navigation_handle,
     ukm::SourceId source_id) {
   current_url_ = navigation_handle->GetURL();
   view_type_ = GetAMPViewType(current_url_);
+  ProcessMainFrameNavigation(navigation_handle, view_type_);
   return CONTINUE_OBSERVING;
 }
 
@@ -101,17 +149,71 @@
     return;
   current_url_ = url;
 
+  // We're transitioning to a new URL, so record metrics for the previous AMP
+  // document, if any.
+  MaybeRecordAmpDocumentMetrics();
+  current_main_frame_nav_info_ = nullptr;
+
   AMPViewType same_document_view_type = GetAMPViewType(url);
   if (same_document_view_type == AMPViewType::NONE)
     return;
 
-  // Though we're not currently able to track page load metrics such as FCP for
-  // same-document navigations, we can count how often they happen, to better
-  // understand the relative frequency of same-document vs new-document AMP
-  // navigations.
+  // Count how often AMP same-document navigations occur.
   UMA_HISTOGRAM_ENUMERATION(
       std::string(kHistogramPrefix).append("SameDocumentView"),
       same_document_view_type, AMPViewType::AMP_VIEW_TYPE_LAST);
+
+  ProcessMainFrameNavigation(navigation_handle, same_document_view_type);
+}
+
+void AMPPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->HasCommitted())
+    return;
+
+  // A new navigation is committing, so ensure any old information associated
+  // with this frame is discarded.
+  amp_subframe_info_.erase(navigation_handle->GetRenderFrameHost());
+
+  // Only track frames that are direct descendents of the main frame.
+  if (navigation_handle->GetParentFrame() == nullptr ||
+      navigation_handle->GetParentFrame()->GetParent() != nullptr)
+    return;
+
+  // Only track frames that have AMP cache URLs.
+  if (GetAMPViewType(navigation_handle->GetURL()) != AMPViewType::AMP_CACHE)
+    return;
+
+  GURL viewer_url = GetViewerUrlFromCacheUrl(navigation_handle->GetURL());
+  if (viewer_url.is_empty())
+    return;
+
+  // Record information about the AMP document loaded in this subframe, which we
+  // may use later to record metrics.
+  auto& subframe_info =
+      amp_subframe_info_[navigation_handle->GetRenderFrameHost()];
+  subframe_info.viewer_url = viewer_url;
+  subframe_info.navigation_start = navigation_handle->NavigationStart();
+
+  // If the current MainFrameNavigationInfo doesn't yet have a subframe
+  // RenderFrameHost, and its URL matches our viewer URL, then associate the
+  // MainFrameNavigationInfo with this frame.
+  if (current_main_frame_nav_info_ &&
+      current_main_frame_nav_info_->subframe_rfh == nullptr &&
+      subframe_info.viewer_url == current_main_frame_nav_info_->url) {
+    current_main_frame_nav_info_->subframe_rfh =
+        navigation_handle->GetRenderFrameHost();
+  }
+}
+
+void AMPPageLoadMetricsObserver::OnFrameDeleted(content::RenderFrameHost* rfh) {
+  if (current_main_frame_nav_info_ &&
+      current_main_frame_nav_info_->subframe_rfh == rfh) {
+    MaybeRecordAmpDocumentMetrics();
+    current_main_frame_nav_info_->subframe_rfh = nullptr;
+  }
+
+  amp_subframe_info_.erase(rfh);
 }
 
 void AMPPageLoadMetricsObserver::OnDomContentLoadedEventStart(
@@ -186,7 +288,7 @@
     // case in the Google News AMP viewer, for example: when a user loads a news
     // AMP URL in a non-same-document navigation context, the user is presented
     // with a redirect prompt which they must click through to continue to the
-    // canoncial document on the non-AMP origin.
+    // canonical document on the non-AMP origin.
     AMPViewType initial_view_type = GetAMPViewType(info.start_url);
     if (initial_view_type != AMPViewType::NONE) {
       RECORD_HISTOGRAM_FOR_TYPE(kHistogramAMPParseStartRedirect,
@@ -200,6 +302,166 @@
                             timing.parse_timing->parse_start.value());
 }
 
+void AMPPageLoadMetricsObserver::OnTimingUpdate(
+    content::RenderFrameHost* subframe_rfh,
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  if (subframe_rfh == nullptr)
+    return;
+
+  auto it = amp_subframe_info_.find(subframe_rfh);
+  if (it == amp_subframe_info_.end())
+    return;
+
+  it->second.timing = timing.Clone();
+}
+
+void AMPPageLoadMetricsObserver::OnComplete(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  MaybeRecordAmpDocumentMetrics();
+  current_main_frame_nav_info_ = nullptr;
+}
+
+void AMPPageLoadMetricsObserver::ProcessMainFrameNavigation(
+    content::NavigationHandle* navigation_handle,
+    AMPViewType view_type) {
+  if (view_type != AMPViewType::GOOGLE_SEARCH_AMP_VIEWER)
+    return;
+
+  // Find the subframe RenderFrameHost hosting the AMP document for this
+  // navigation. Note that in some cases, the subframe may not exist yet, in
+  // which case logic in OnDidFinishSubFrameNavigation will associate the
+  // subframe with current_main_frame_nav_info_.
+  content::RenderFrameHost* subframe_rfh = nullptr;
+  for (const auto& kv : amp_subframe_info_) {
+    if (navigation_handle->GetURL() == kv.second.viewer_url) {
+      subframe_rfh = kv.first;
+      break;
+    }
+  }
+
+  current_main_frame_nav_info_ = base::WrapUnique(
+      new MainFrameNavigationInfo{navigation_handle->GetURL(), subframe_rfh,
+                                  navigation_handle->NavigationStart(),
+                                  navigation_handle->IsSameDocument()});
+}
+
+void AMPPageLoadMetricsObserver::MaybeRecordAmpDocumentMetrics() {
+  if (current_main_frame_nav_info_ == nullptr ||
+      current_main_frame_nav_info_->subframe_rfh == nullptr)
+    return;
+
+  auto it = amp_subframe_info_.find(current_main_frame_nav_info_->subframe_rfh);
+  if (it == amp_subframe_info_.end())
+    return;
+
+  const SubFrameInfo& subframe_info = it->second;
+  if (subframe_info.viewer_url != current_main_frame_nav_info_->url)
+    return;
+
+  // TimeDeltas in subframe_info are relative to the navigation start in the AMP
+  // subframe. Given that AMP subframes can be prerendered and thus their
+  // navigation start may be long before a user initiates the navigation to that
+  // AMP document, we need to adjust the times by the difference between the
+  // top-level navigation start (which is when the top-level URL was updated to
+  // reflect the AMP Viewer URL for the AMP document) and the navigation start
+  // in the AMP subframe. Note that we use the top-level navigation start as our
+  // best estimate of when the user initiated the navigation.
+  base::TimeDelta navigation_input_delta =
+      current_main_frame_nav_info_->navigation_start -
+      subframe_info.navigation_start;
+
+  if (!current_main_frame_nav_info_->is_same_document_navigation) {
+    // For non same document navigations, we expect the main frame navigation
+    // to be before the subframe navigation. This measures the time from main
+    // frame navigation to the time the AMP subframe is added to the document.
+    PAGE_LOAD_HISTOGRAM(
+        std::string(kHistogramPrefix)
+            .append(kHistogramAMPSubframeMainFrameToSubFrameNavigation),
+        -navigation_input_delta);
+  } else {
+    if (navigation_input_delta >= base::TimeDelta()) {
+      // Prerender case: subframe navigation happens before main frame
+      // navigation.
+      PAGE_LOAD_HISTOGRAM(std::string(kHistogramPrefix)
+                              .append(kHistogramAMPSubframeNavigationToInput),
+                          navigation_input_delta);
+    } else {
+      // For same document navigations, if the main frame navigation is
+      // initiated before the AMP subframe is navigated,
+      // |navigation_input_delta| will be negative. This happens in the
+      // non-prerender case. We record this delta to ensure it's consistently a
+      // small value (the expected case).
+      PAGE_LOAD_HISTOGRAM(std::string(kHistogramPrefix)
+                              .append(kHistogramAMPSubframeInputToNavigation),
+                          -navigation_input_delta);
+    }
+  }
+
+  if (!subframe_info.timing.is_null()) {
+    if (subframe_info.timing->paint_timing->first_contentful_paint
+            .has_value()) {
+      base::TimeDelta first_contentful_paint = ClampToZero(
+          subframe_info.timing->paint_timing->first_contentful_paint.value() -
+          navigation_input_delta);
+      if (current_main_frame_nav_info_->is_same_document_navigation) {
+        PAGE_LOAD_HISTOGRAM(
+            std::string(kHistogramPrefix)
+                .append(kHistogramAMPSubframeFirstContentfulPaint),
+            first_contentful_paint);
+      } else {
+        PAGE_LOAD_HISTOGRAM(
+            std::string(kHistogramPrefix)
+                .append(
+                    kHistogramAMPSubframeFirstContentfulPaintFullNavigation),
+            first_contentful_paint);
+      }
+    }
+
+    base::Optional<base::TimeDelta> largest_content_paint_time;
+    uint64_t largest_content_paint_size;
+    PageLoadMetricsObserver::LargestContentType largest_content_type;
+    if (AssignTimeAndSizeForLargestContentfulPaint(
+            subframe_info.timing->paint_timing, &largest_content_paint_time,
+            &largest_content_paint_size, &largest_content_type)) {
+      // Adjust by the navigation_input_delta.
+      largest_content_paint_time = ClampToZero(
+          largest_content_paint_time.value() - navigation_input_delta);
+      if (current_main_frame_nav_info_->is_same_document_navigation) {
+        PAGE_LOAD_HISTOGRAM(
+            std::string(kHistogramPrefix)
+                .append(kHistogramAMPSubframeLargestContentPaint),
+            largest_content_paint_time.value());
+      } else {
+        PAGE_LOAD_HISTOGRAM(
+            std::string(kHistogramPrefix)
+                .append(kHistogramAMPSubframeLargestContentPaintFullNavigation),
+            largest_content_paint_time.value());
+      }
+    }
+
+    if (subframe_info.timing->interactive_timing->first_input_delay
+            .has_value()) {
+      if (current_main_frame_nav_info_->is_same_document_navigation) {
+        UMA_HISTOGRAM_CUSTOM_TIMES(
+            std::string(kHistogramPrefix)
+                .append(kHistogramAMPSubframeFirstInputDelay),
+            subframe_info.timing->interactive_timing->first_input_delay.value(),
+            base::TimeDelta::FromMilliseconds(1),
+            base::TimeDelta::FromSeconds(60), 50);
+      } else {
+        UMA_HISTOGRAM_CUSTOM_TIMES(
+            std::string(kHistogramPrefix)
+                .append(kHistogramAMPSubframeFirstInputDelayFullNavigation),
+            subframe_info.timing->interactive_timing->first_input_delay.value(),
+            base::TimeDelta::FromMilliseconds(1),
+            base::TimeDelta::FromSeconds(60), 50);
+      }
+    }
+  }
+}
+
 // static
 AMPPageLoadMetricsObserver::AMPViewType
 AMPPageLoadMetricsObserver::GetAMPViewType(const GURL& url) {
diff --git a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h
index 4c6f289..408e969c 100644
--- a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AMP_PAGE_LOAD_METRICS_OBSERVER_H_
 #define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AMP_PAGE_LOAD_METRICS_OBSERVER_H_
 
+#include <map>
+#include <memory>
+
 #include "base/macros.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
 #include "services/metrics/public/cpp/ukm_source.h"
@@ -13,12 +16,31 @@
 class NavigationHandle;
 }
 
-// Observer responsible for recording page load metrics relevant to page served
-// from the AMP cache. When AMP pages are served in a same page navigation, UMA
-// is not recorded; this is typical for AMP pages navigated to from google.com.
-// Navigations to AMP pages from the google search app or directly to the amp
-// cache page will be tracked. Refreshing an AMP page served from google.com
-// will be tracked.
+// Observer responsible for recording metrics for AMP documents. This includes
+// both AMP documents loaded in the main frame, and AMP documents loaded in a
+// subframe (e.g. in an AMP viewer in the main frame).
+//
+// For AMP documents loaded in a subframe, recording works like so:
+//
+// * whenever the main frame URL gets updated with an AMP viewer
+//   URL (e.g. https://www.google.com/amp/...), we track that in
+//   OnCommitSameDocumentNavigation. we use the time of the main
+//   frame URL update as the baseline time from which the
+//   user-perceived performance metrics like FCP are computed.
+//
+// * whenever an iframe AMP document is loaded, we track that in
+//   OnDidFinishSubFrameNavigation. we also keep track of the
+//   performance timing metrics such as FCP reported in the frame.
+//
+// * we associate the main frame AMP navigation with its associated
+//   AMP document in an iframe by comparing URLs between the main
+//   frame and the iframe documents.
+//
+// * we record AMP metrics at the times when an AMP viewer URL is
+//   navigated away from. when a user swipes to change the AMP
+//   document, closes a tab, types a new URL in the URL bar, etc,
+//   we will record AMP metrics for the AMP doc in an iframe that
+//   the user is navigating away from.
 class AMPPageLoadMetricsObserver
     : public page_load_metrics::PageLoadMetricsObserver {
  public:
@@ -45,6 +67,9 @@
                          ukm::SourceId source_id) override;
   void OnCommitSameDocumentNavigation(
       content::NavigationHandle* navigation_handle) override;
+  void OnDidFinishSubFrameNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void OnFrameDeleted(content::RenderFrameHost* rfh) override;
   void OnDomContentLoadedEventStart(
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& info) override;
@@ -58,8 +83,60 @@
       const page_load_metrics::PageLoadExtraInfo& info) override;
   void OnParseStart(const page_load_metrics::mojom::PageLoadTiming& timing,
                     const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnTimingUpdate(
+      content::RenderFrameHost* subframe_rfh,
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
+                  const page_load_metrics::PageLoadExtraInfo& info) override;
 
  private:
+  // Information about an AMP navigation in the main frame. Both regular and
+  // same document navigations are included.
+  struct MainFrameNavigationInfo {
+    GURL url;
+
+    // Pointer to the RenderViewHost for the iframe hosting the AMP document
+    // associated with the main frame AMP navigation.
+    content::RenderFrameHost* subframe_rfh = nullptr;
+
+    // Navigation start time for the main frame AMP navigation. We use this time
+    // as an approximation of the time the user initiated the navigation.
+    base::TimeTicks navigation_start;
+
+    // Whether the main frame navigation is a same document navigation.
+    bool is_same_document_navigation = false;
+  };
+
+  // Information about an AMP subframe.
+  struct SubFrameInfo {
+    SubFrameInfo();
+    ~SubFrameInfo();
+
+    // The AMP viewer URL of the currently committed AMP document. Used for
+    // matching the MainFrameNavigationInfo url.
+    GURL viewer_url;
+
+    // The navigation start time of the non-same-document navigation in the AMP
+    // iframe. Timestamps in |timing| below are relative to this value.
+    base::TimeTicks navigation_start;
+
+    // Timing metrics observed in the AMP iframe.
+    page_load_metrics::mojom::PageLoadTimingPtr timing;
+  };
+
+  void ProcessMainFrameNavigation(content::NavigationHandle* navigation_handle,
+                                  AMPViewType view_type);
+  void MaybeRecordAmpDocumentMetrics();
+
+  // Information about the currently active AMP navigation in the main
+  // frame. Will be null if there isn't an active AMP navigation in the main
+  // frame.
+  std::unique_ptr<MainFrameNavigationInfo> current_main_frame_nav_info_;
+
+  // Information about each active AMP iframe in the main document.
+  std::map<content::RenderFrameHost*, SubFrameInfo> amp_subframe_info_;
+
   GURL current_url_;
   AMPViewType view_type_ = AMPViewType::NONE;
 
diff --git a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc
index 9b7a666..21b2d05d 100644
--- a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc
@@ -13,7 +13,9 @@
 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
 #include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_utils.h"
 #include "url/gurl.h"
 
 using content::NavigationSimulator;
@@ -205,6 +207,18 @@
       static_cast<base::HistogramBase::Sample>(
           AMPViewType::GOOGLE_SEARCH_AMP_VIEWER),
       2);
+
+  // Verify that subframe metrics aren't recorded without an AMP subframe.
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
 }
 
 TEST_F(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCacheRedirect) {
@@ -231,3 +245,383 @@
           timing_.parse_timing->parse_start.value().InMilliseconds()),
       1);
 }
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameInputBeforeNavigation) {
+  // This emulates the AMP subframe non-prerender flow: first we perform a
+  // same-document navigation in the main frame to the AMP viewer URL, then we
+  // create and navigate the subframe to an AMP cache URL.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page"
+           "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameNavigationBeforeInput) {
+  // This emulates the AMP subframe prerender flow: first we create and navigate
+  // the subframe to an AMP cache URL, then we perform a same-document
+  // navigation in the main frame to the AMP viewer URL.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page"
+           "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMetrics) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  content::RenderFrameHost* subframe =
+      NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL("https://cdn.ampproject.org/page"
+               "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+          content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+              ->AppendChild("subframe"));
+
+  page_load_metrics::mojom::PageLoadTiming subframe_timing;
+  page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
+  subframe_timing.navigation_start = base::Time::FromDoubleT(2);
+  subframe_timing.paint_timing->first_contentful_paint =
+      base::TimeDelta::FromMilliseconds(5);
+  subframe_timing.paint_timing->largest_image_paint_size = 1;
+  subframe_timing.paint_timing->largest_image_paint =
+      base::TimeDelta::FromMilliseconds(10);
+  subframe_timing.interactive_timing->first_input_timestamp =
+      base::TimeDelta::FromMilliseconds(20);
+  subframe_timing.interactive_timing->first_input_delay =
+      base::TimeDelta::FromMilliseconds(3);
+  PopulateRequiredTimingFields(&subframe_timing);
+
+  SimulateTimingUpdate(subframe_timing, subframe);
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.PaintTiming.InputToLargestContentPaint.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay3.Subframe", 1);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMetricsFullNavigation) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->Commit();
+
+  content::RenderFrameHost* subframe =
+      NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL("https://cdn.ampproject.org/page"
+               "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+          content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+              ->AppendChild("subframe"));
+
+  page_load_metrics::mojom::PageLoadTiming subframe_timing;
+  page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
+  subframe_timing.navigation_start = base::Time::FromDoubleT(2);
+  subframe_timing.paint_timing->first_contentful_paint =
+      base::TimeDelta::FromMilliseconds(5);
+  subframe_timing.paint_timing->largest_image_paint_size = 1;
+  subframe_timing.paint_timing->largest_image_paint =
+      base::TimeDelta::FromMilliseconds(10);
+  subframe_timing.interactive_timing->first_input_timestamp =
+      base::TimeDelta::FromMilliseconds(20);
+  subframe_timing.interactive_timing->first_input_delay =
+      base::TimeDelta::FromMilliseconds(3);
+  PopulateRequiredTimingFields(&subframe_timing);
+
+  SimulateTimingUpdate(subframe_timing, subframe);
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe."
+      "FullNavigation",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.PaintTiming.InputToLargestContentPaint.Subframe."
+      "FullNavigation",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay3.Subframe."
+      "FullNavigation",
+      1);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameRecordOnFullNavigation) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page"
+           "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(GURL("https://www.example.com/"),
+                                               main_rfh())
+      ->Commit();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      1);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameRecordOnFrameDeleted) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  content::RenderFrameHost* subframe =
+      NavigationSimulator::NavigateAndCommitFromDocument(
+          GURL("https://cdn.ampproject.org/page"
+               "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+          content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+              ->AppendChild("subframe"));
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      0);
+
+  // Delete the subframe, which should trigger metrics recording.
+  content::RenderFrameHostTester::For(subframe)->Detach();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      1);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMultipleFrames) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  // Simulate a prerender.
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page2"
+           "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage2"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Perform a main-frame navigation to a different AMP document (not the
+  // prerender).
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  // Load the associated AMP document in an iframe.
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page"
+           "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Navigate the main frame to trigger metrics recording - we expect metrics to
+  // have been recorded for 1 AMP page (the non-prerendered page).
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
+
+  // Now navigate to the main-frame URL for the prerendered AMP document. This
+  // should trigger metrics recording for that document.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page2"), main_rfh())
+      ->CommitSameDocument();
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  // We now expect one NavigationToInput (for the prerender) and one
+  // InputToNavigation (for the non-prerender).
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      1);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest,
+       SubFrameWithNonSameDocumentMainFrameNavigation) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->Commit();
+
+  // Load the associated AMP document in an iframe.
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page"
+           "#viewerUrl=https%3A%2F%2Fwww.google.com%2Famp%2Fpage"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Navigate the main frame to trigger metrics recording - we expect metrics to
+  // have been recorded for 1 AMP page (the non-prerendered page).
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      1);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest,
+       NoSubFrameMetricsForSubFrameWithNonAmpUrl) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  // Create a non-AMP subframe document.
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://example.com/"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
+}
+
+TEST_F(AMPPageLoadMetricsObserverTest,
+       NoSubFrameMetricsForSubFrameWithoutViewerUrl) {
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/search"), main_rfh())
+      ->Commit();
+
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/page"), main_rfh())
+      ->CommitSameDocument();
+
+  NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("https://cdn.ampproject.org/page"),
+      content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
+          ->AppendChild("subframe"));
+
+  // Navigate the main frame to trigger metrics recording.
+  NavigationSimulator::CreateRendererInitiated(
+      GURL("https://www.google.com/amp/other"), main_rfh())
+      ->CommitSameDocument();
+
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
+      0);
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.AMP.Experimental.PageTiming."
+      "MainFrameToSubFrameNavigationDelta.Subframe",
+      0);
+}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_observer.h b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
index e9afc6f..86d2b9f 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/page_load_metrics_observer.h
@@ -532,6 +532,8 @@
   virtual void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
                                 const gfx::Size& frame_size) {}
 
+  virtual void OnFrameDeleted(content::RenderFrameHost* render_frame_host) {}
+
   // Called when the event corresponding to |event_key| occurs in this page
   // load.
   virtual void OnEventOccurred(const void* const event_key) {}
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.cc b/chrome/browser/page_load_metrics/page_load_tracker.cc
index b0c3f4b..a64c199a 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.cc
+++ b/chrome/browser/page_load_metrics/page_load_tracker.cc
@@ -325,6 +325,12 @@
   INVOKE_AND_PRUNE_OBSERVERS(observers_, OnShown);
 }
 
+void PageLoadTracker::FrameDeleted(content::RenderFrameHost* rfh) {
+  for (const auto& observer : observers_) {
+    observer->OnFrameDeleted(rfh);
+  }
+}
+
 void PageLoadTracker::WillProcessNavigationResponse(
     content::NavigationHandle* navigation_handle) {
   DCHECK(!navigation_request_id_.has_value());
diff --git a/chrome/browser/page_load_metrics/page_load_tracker.h b/chrome/browser/page_load_metrics/page_load_tracker.h
index 8290dcd..7c898ed 100644
--- a/chrome/browser/page_load_metrics/page_load_tracker.h
+++ b/chrome/browser/page_load_metrics/page_load_tracker.h
@@ -200,6 +200,7 @@
                              base::TimeTicks failed_load_time);
   void WebContentsHidden();
   void WebContentsShown();
+  void FrameDeleted(content::RenderFrameHost* rfh);
 
   void OnInputEvent(const blink::WebInputEvent& event);
 
diff --git a/chrome/browser/profiles/profile_downloader.cc b/chrome/browser/profiles/profile_downloader.cc
index 6146898..b400d9e 100644
--- a/chrome/browser/profiles/profile_downloader.cc
+++ b/chrome/browser/profiles/profile_downloader.cc
@@ -17,6 +17,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_downloader_delegate.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -120,10 +121,10 @@
   if (maybe_account_info.has_value())
     account_info_ = maybe_account_info.value();
 
-  if (delegate_->IsPreSignin()) {
-    AccountFetcherServiceFactory::GetForProfile(delegate_->GetBrowserProfile())
-        ->FetchUserInfoBeforeSignin(account_id_);
-  }
+#if defined(OS_ANDROID)
+  if (delegate_->IsPreSignin())
+    identity_manager_->ForceRefreshOfExtendedAccountInfo(account_id_);
+#endif
 
   if (account_info_.IsValid()) {
     // FetchImageData might call the delegate's OnProfileDownloadSuccess
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index a1df4ad8..22dcfe3 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -85,8 +85,6 @@
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/signin_ui_util.h"
 #include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h"
 #include "chrome/browser/ssl/chrome_ssl_host_state_delegate_factory.h"
@@ -1255,9 +1253,7 @@
   if (service_name == identity::mojom::kServiceName) {
     return std::make_unique<identity::IdentityService>(
         IdentityManagerFactory::GetForProfile(this),
-        AccountTrackerServiceFactory::GetForProfile(this),
-        ProfileOAuth2TokenServiceFactory::GetForProfile(this),
-        std::move(request));
+        AccountTrackerServiceFactory::GetForProfile(this), std::move(request));
   }
 
   if (service_name == prefs::mojom::kServiceName) {
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index 26b71ba..8051b83d 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -136,6 +136,12 @@
   /** @private {boolean} */
   this.hasEnteredAnnotationMode_ = false;
 
+  /** @private {boolean} */
+  this.hadPassword_ = false;
+
+  /** @private {boolean} */
+  this.canSerializeDocument_ = false;
+
   PDFMetrics.record(PDFMetrics.UserAction.DOCUMENT_OPENED);
 
   // Parse open pdf parameters.
@@ -1074,6 +1080,8 @@
     // If the password screen isn't up, put it up. Otherwise we're
     // responding to an incorrect password so deny it.
     if (!this.passwordScreen_.active) {
+      this.hadPassword_ = true;
+      this.updateAnnotationAvailable_();
       this.passwordScreen_.show();
     } else {
       this.passwordScreen_.deny();
@@ -1119,8 +1127,9 @@
    * Sets document metadata from the current controller.
    * @param {string} title
    * @param {Array} bookmarks
+   * @param {boolean} canSerializeDocument
    */
-  setDocumentMetadata: function(title, bookmarks) {
+  setDocumentMetadata: function(title, bookmarks, canSerializeDocument) {
     if (title) {
       document.title = title;
     } else {
@@ -1131,6 +1140,8 @@
       this.toolbar_.docTitle = document.title;
       this.toolbar_.bookmarks = this.bookmarks;
     }
+    this.canSerializeDocument_ = canSerializeDocument;
+    this.updateAnnotationAvailable_();
   },
 
   /**
@@ -1227,6 +1238,12 @@
     if (this.viewport_.getClockwiseRotations() != 0) {
       annotationAvailable = false;
     }
+    if (this.hadPassword_) {
+      annotationAvailable = false;
+    }
+    if (!this.canSerializeDocument_) {
+      annotationAvailable = false;
+    }
     this.toolbar_.annotationAvailable = annotationAvailable;
   },
 
@@ -1563,7 +1580,8 @@
         break;
       case 'metadata':
         this.viewer_.setDocumentMetadata(
-            message.data.title, message.data.bookmarks);
+            message.data.title, message.data.bookmarks,
+            message.data.canSerializeDocument);
         break;
       case 'setIsSelecting':
         this.viewer_.setIsSelecting(message.data.isSelecting);
diff --git a/chrome/browser/security_events/BUILD.gn b/chrome/browser/security_events/BUILD.gn
new file mode 100644
index 0000000..924a1db
--- /dev/null
+++ b/chrome/browser/security_events/BUILD.gn
@@ -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.
+
+static_library("security_events") {
+  sources = [
+    "security_event_recorder.h",
+    "security_event_recorder_impl.cc",
+    "security_event_recorder_impl.h",
+    "security_event_sync_bridge.h",
+    "security_event_sync_bridge_impl.cc",
+    "security_event_sync_bridge_impl.h",
+  ]
+
+  deps = [
+    "//components/keyed_service/core",
+    "//components/sync",
+  ]
+}
diff --git a/chrome/browser/security_events/OWNERS b/chrome/browser/security_events/OWNERS
new file mode 100644
index 0000000..13cac5a6
--- /dev/null
+++ b/chrome/browser/security_events/OWNERS
@@ -0,0 +1,7 @@
+# For Safebrowsing related reviews
+vakh@chromium.org
+# For Chrome Sync related reviews
+markusheintz@chromium.org
+
+# COMPONENT: Services>Safebrowsing
+
diff --git a/chrome/browser/security_events/README.md b/chrome/browser/security_events/README.md
new file mode 100644
index 0000000..3ab93ed
--- /dev/null
+++ b/chrome/browser/security_events/README.md
@@ -0,0 +1,6 @@
+# Security Event Recorder
+
+The security event recorder is a service for recording security related events.
+Security events are recorded for signed in users and are used to determine the
+threat level for a user account.
+
diff --git a/chrome/browser/security_events/security_event_recorder.h b/chrome/browser/security_events/security_event_recorder.h
new file mode 100644
index 0000000..de9411e
--- /dev/null
+++ b/chrome/browser/security_events/security_event_recorder.h
@@ -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.
+
+#ifndef CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_RECORDER_H_
+#define CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_RECORDER_H_
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/sync/model/model_type_controller_delegate.h"
+#include "components/sync/protocol/sync.pb.h"
+
+// The SecurityEventRecorder class allows to record security events via Chrome
+// Sync for signed-in users.
+class SecurityEventRecorder : public KeyedService {
+ public:
+  SecurityEventRecorder() = default;
+  ~SecurityEventRecorder() override = default;
+
+  // Records the GaiaPasswordReuse security event for the currently signed-in
+  // user. The event is recorded via Chrome Sync.
+  virtual void RecordGaiaPasswordReuse(
+      const sync_pb::GaiaPasswordReuse& event) = 0;
+
+  // Returns the underlying Sync integration point.
+  virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
+  GetControllerDelegate() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SecurityEventRecorder);
+};
+
+#endif  // CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_RECORDER_H_
diff --git a/chrome/browser/security_events/security_event_recorder_impl.cc b/chrome/browser/security_events/security_event_recorder_impl.cc
new file mode 100644
index 0000000..31e9a58
--- /dev/null
+++ b/chrome/browser/security_events/security_event_recorder_impl.cc
@@ -0,0 +1,42 @@
+// 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/security_events/security_event_recorder_impl.h"
+
+#include <memory>
+#include <utility>
+
+using sync_pb::SecurityEventSpecifics;
+
+SecurityEventRecorderImpl::SecurityEventRecorderImpl(
+    std::unique_ptr<SecurityEventSyncBridge> security_event_sync_bridge,
+    base::Clock* clock)
+    : security_event_sync_bridge_(std::move(security_event_sync_bridge)),
+      clock_(clock) {
+  DCHECK(security_event_sync_bridge_);
+  DCHECK(clock_);
+}
+
+SecurityEventRecorderImpl::~SecurityEventRecorderImpl() {}
+
+void SecurityEventRecorderImpl::RecordGaiaPasswordReuse(
+    const sync_pb::GaiaPasswordReuse& event) {
+  sync_pb::SecurityEventSpecifics specifics;
+  specifics.set_event_time_usec(clock_->Now().since_origin().InMicroseconds());
+  sync_pb::GaiaPasswordReuse* gaia_password_reuse_event =
+      specifics.mutable_gaia_password_reuse_event();
+  gaia_password_reuse_event->CopyFrom(event);
+
+  security_event_sync_bridge_->RecordSecurityEvent(std::move(specifics));
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+SecurityEventRecorderImpl::GetControllerDelegate() {
+  if (security_event_sync_bridge_) {
+    return security_event_sync_bridge_->GetControllerDelegate();
+  }
+  return base::WeakPtr<syncer::ModelTypeControllerDelegate>();
+}
+
+void SecurityEventRecorderImpl::Shutdown() {}
diff --git a/chrome/browser/security_events/security_event_recorder_impl.h b/chrome/browser/security_events/security_event_recorder_impl.h
new file mode 100644
index 0000000..c3e79ee
--- /dev/null
+++ b/chrome/browser/security_events/security_event_recorder_impl.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 CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_RECORDER_IMPL_H_
+#define CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_RECORDER_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/time/clock.h"
+#include "chrome/browser/security_events/security_event_recorder.h"
+#include "chrome/browser/security_events/security_event_sync_bridge.h"
+
+class SecurityEventRecorderImpl : public SecurityEventRecorder {
+ public:
+  SecurityEventRecorderImpl(
+      std::unique_ptr<SecurityEventSyncBridge> security_event_sync_bridge,
+      base::Clock* clock);
+  ~SecurityEventRecorderImpl() override;
+
+  void RecordGaiaPasswordReuse(
+      const sync_pb::GaiaPasswordReuse& event) override;
+
+  base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate()
+      override;
+
+  // KeyedService (through SecurityEventRecorder) implementation.
+  void Shutdown() override;
+
+ private:
+  std::unique_ptr<SecurityEventSyncBridge> security_event_sync_bridge_;
+  base::Clock* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(SecurityEventRecorderImpl);
+};
+
+#endif  // CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_RECORDER_IMPL_H_
diff --git a/chrome/browser/security_events/security_event_sync_bridge.h b/chrome/browser/security_events/security_event_sync_bridge.h
new file mode 100644
index 0000000..5a01860
--- /dev/null
+++ b/chrome/browser/security_events/security_event_sync_bridge.h
@@ -0,0 +1,30 @@
+// 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_SECURITY_EVENTS_SECURITY_EVENT_SYNC_BRIDGE_H_
+#define CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_SYNC_BRIDGE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "components/sync/model/model_type_controller_delegate.h"
+#include "components/sync/protocol/sync.pb.h"
+
+class SecurityEventSyncBridge {
+ public:
+  SecurityEventSyncBridge() = default;
+  virtual ~SecurityEventSyncBridge() = default;
+
+  virtual void RecordSecurityEvent(
+      sync_pb::SecurityEventSpecifics specifics) = 0;
+
+  // Returns the delegate for the controller, i.e. sync integration point.
+  virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
+  GetControllerDelegate() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SecurityEventSyncBridge);
+};
+
+#endif  // CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_SYNC_BRIDGE_H_
diff --git a/chrome/browser/security_events/security_event_sync_bridge_impl.cc b/chrome/browser/security_events/security_event_sync_bridge_impl.cc
new file mode 100644
index 0000000..26fbfa1e
--- /dev/null
+++ b/chrome/browser/security_events/security_event_sync_bridge_impl.cc
@@ -0,0 +1,218 @@
+// 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/security_events/security_event_sync_bridge_impl.h"
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/big_endian.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mutable_data_batch.h"
+
+namespace {
+
+std::string GetStorageKeyFromSpecifics(
+    const sync_pb::SecurityEventSpecifics& specifics) {
+  // Force Big Endian, this means newly created keys are last in sort order,
+  // which allows leveldb to append new writes, which it is best at.
+  // TODO(markusheintz): Until we force |event_time_usec| to never conflict,
+  // this has the potential for errors.
+  std::string key(8, 0);
+  base::WriteBigEndian(&key[0], specifics.event_time_usec());
+  return key;
+}
+
+std::unique_ptr<syncer::EntityData> ToEntityData(
+    sync_pb::SecurityEventSpecifics specifics) {
+  auto entity_data = std::make_unique<syncer::EntityData>();
+  entity_data->non_unique_name =
+      base::NumberToString(specifics.event_time_usec());
+  entity_data->specifics.set_allocated_security_event(&specifics);
+  return entity_data;
+}
+
+}  // namespace
+
+SecurityEventSyncBridgeImpl::SecurityEventSyncBridgeImpl(
+    syncer::OnceModelTypeStoreFactory store_factory,
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
+    : syncer::ModelTypeSyncBridge(std::move(change_processor)),
+      weak_ptr_factory_(this) {
+  std::move(store_factory)
+      .Run(syncer::SECURITY_EVENTS,
+           base::BindOnce(&SecurityEventSyncBridgeImpl::OnStoreCreated,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+SecurityEventSyncBridgeImpl::~SecurityEventSyncBridgeImpl() {}
+
+void SecurityEventSyncBridgeImpl::RecordSecurityEvent(
+    sync_pb::SecurityEventSpecifics specifics) {
+  if (!store_) {
+    return;
+  }
+  if (!change_processor()->IsTrackingMetadata()) {
+    return;
+  }
+
+  std::string storage_key = GetStorageKeyFromSpecifics(specifics);
+
+  std::unique_ptr<syncer::ModelTypeStore::WriteBatch> write_batch =
+      store_->CreateWriteBatch();
+  write_batch->WriteData(storage_key, specifics.SerializeAsString());
+
+  change_processor()->Put(storage_key, ToEntityData(std::move(specifics)),
+                          write_batch->GetMetadataChangeList());
+
+  store_->CommitWriteBatch(
+      std::move(write_batch),
+      base::BindOnce(&SecurityEventSyncBridgeImpl::OnCommit,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+SecurityEventSyncBridgeImpl::GetControllerDelegate() {
+  return change_processor()->GetControllerDelegate();
+}
+
+std::unique_ptr<syncer::MetadataChangeList>
+SecurityEventSyncBridgeImpl::CreateMetadataChangeList() {
+  return syncer::ModelTypeStore::WriteBatch::CreateMetadataChangeList();
+}
+
+base::Optional<syncer::ModelError> SecurityEventSyncBridgeImpl::MergeSyncData(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_data) {
+  DCHECK(entity_data.empty());
+  DCHECK(change_processor()->IsTrackingMetadata());
+  DCHECK(!change_processor()->TrackedAccountId().empty());
+  return ApplySyncChanges(std::move(metadata_change_list),
+                          std::move(entity_data));
+}
+
+base::Optional<syncer::ModelError>
+SecurityEventSyncBridgeImpl::ApplySyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_changes) {
+  std::unique_ptr<syncer::ModelTypeStore::WriteBatch> write_batch =
+      store_->CreateWriteBatch();
+  for (syncer::EntityChange& change : entity_changes) {
+    DCHECK_EQ(syncer::EntityChange::ACTION_DELETE, change.type());
+    write_batch->DeleteData(change.storage_key());
+  }
+
+  write_batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
+  store_->CommitWriteBatch(
+      std::move(write_batch),
+      base::BindOnce(&SecurityEventSyncBridgeImpl::OnCommit,
+                     weak_ptr_factory_.GetWeakPtr()));
+  return {};
+}
+
+void SecurityEventSyncBridgeImpl::GetData(StorageKeyList storage_keys,
+                                          DataCallback callback) {
+  store_->ReadData(
+      storage_keys,
+      base::BindOnce(&SecurityEventSyncBridgeImpl::OnReadData,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void SecurityEventSyncBridgeImpl::GetAllDataForDebugging(
+    DataCallback callback) {
+  store_->ReadAllData(
+      base::BindOnce(&SecurityEventSyncBridgeImpl::OnReadAllData,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+std::string SecurityEventSyncBridgeImpl::GetClientTag(
+    const syncer::EntityData& entity_data) {
+  return GetStorageKey(entity_data);
+}
+
+std::string SecurityEventSyncBridgeImpl::GetStorageKey(
+    const syncer::EntityData& entity_data) {
+  return GetStorageKeyFromSpecifics(entity_data.specifics.security_event());
+}
+
+void SecurityEventSyncBridgeImpl::ApplyStopSyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) {
+  if (delete_metadata_change_list) {
+    store_->DeleteAllDataAndMetadata(
+        base::BindOnce(&SecurityEventSyncBridgeImpl::OnCommit,
+                       weak_ptr_factory_.GetWeakPtr()));
+  }
+}
+
+void SecurityEventSyncBridgeImpl::OnStoreCreated(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore> store) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  store_ = std::move(store);
+  store_->ReadAllMetadata(
+      base::BindOnce(&SecurityEventSyncBridgeImpl::OnReadAllMetadata,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SecurityEventSyncBridgeImpl::OnReadData(
+    DataCallback callback,
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records,
+    std::unique_ptr<syncer::ModelTypeStore::IdList> missing_id_list) {
+  OnReadAllData(std::move(callback), error, std::move(data_records));
+}
+
+void SecurityEventSyncBridgeImpl::OnReadAllData(
+    DataCallback callback,
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
+  for (const syncer::ModelTypeStore::Record& r : *data_records) {
+    sync_pb::SecurityEventSpecifics specifics;
+
+    if (specifics.ParseFromString(r.value)) {
+      DCHECK_EQ(r.id, GetStorageKeyFromSpecifics(specifics));
+      batch->Put(r.id, ToEntityData(std::move(specifics)));
+    } else {
+      change_processor()->ReportError(
+          {FROM_HERE, "Failed deserializing security events."});
+      return;
+    }
+  }
+  std::move(callback).Run(std::move(batch));
+}
+
+void SecurityEventSyncBridgeImpl::OnReadAllMetadata(
+    const base::Optional<syncer::ModelError>& error,
+    std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
+  if (error) {
+    change_processor()->ReportError(*error);
+  } else {
+    change_processor()->ModelReadyToSync(std::move(metadata_batch));
+  }
+}
+
+void SecurityEventSyncBridgeImpl::OnCommit(
+    const base::Optional<syncer::ModelError>& error) {
+  if (error) {
+    change_processor()->ReportError(*error);
+  }
+}
diff --git a/chrome/browser/security_events/security_event_sync_bridge_impl.h b/chrome/browser/security_events/security_event_sync_bridge_impl.h
new file mode 100644
index 0000000..8708b143
--- /dev/null
+++ b/chrome/browser/security_events/security_event_sync_bridge_impl.h
@@ -0,0 +1,76 @@
+// 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_SECURITY_EVENTS_SECURITY_EVENT_SYNC_BRIDGE_IMPL_H_
+#define CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_SYNC_BRIDGE_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chrome/browser/security_events/security_event_sync_bridge.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+#include "components/sync/protocol/sync.pb.h"
+
+class SecurityEventSyncBridgeImpl : public SecurityEventSyncBridge,
+                                    public syncer::ModelTypeSyncBridge {
+ public:
+  SecurityEventSyncBridgeImpl(
+      syncer::OnceModelTypeStoreFactory store_factory,
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
+  ~SecurityEventSyncBridgeImpl() override;
+
+  void RecordSecurityEvent(sync_pb::SecurityEventSpecifics specifics) override;
+
+  base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate()
+      override;
+
+  // ModelTypeSyncBridge implementation.
+  std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+      override;
+  base::Optional<syncer::ModelError> MergeSyncData(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_data) override;
+  base::Optional<syncer::ModelError> ApplySyncChanges(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_changes) override;
+  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetAllDataForDebugging(DataCallback callback) override;
+  std::string GetClientTag(const syncer::EntityData& entity_data) override;
+  std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+  void ApplyStopSyncChanges(std::unique_ptr<syncer::MetadataChangeList>
+                                delete_metadata_change_list) override;
+
+ private:
+  void OnStoreCreated(const base::Optional<syncer::ModelError>& error,
+                      std::unique_ptr<syncer::ModelTypeStore> store);
+
+  void OnReadData(
+      DataCallback callback,
+      const base::Optional<syncer::ModelError>& error,
+      std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records,
+      std::unique_ptr<syncer::ModelTypeStore::IdList> missing_id_list);
+
+  void OnReadAllData(
+      DataCallback callback,
+      const base::Optional<syncer::ModelError>& error,
+      std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records);
+
+  void OnReadAllMetadata(const base::Optional<syncer::ModelError>& error,
+                         std::unique_ptr<syncer::MetadataBatch> metadata_batch);
+
+  void OnCommit(const base::Optional<syncer::ModelError>& error);
+
+  std::unique_ptr<syncer::ModelTypeStore> store_;
+
+  base::WeakPtrFactory<SecurityEventSyncBridgeImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SecurityEventSyncBridgeImpl);
+};
+
+#endif  // CHROME_BROWSER_SECURITY_EVENTS_SECURITY_EVENT_SYNC_BRIDGE_IMPL_H_
diff --git a/chrome/browser/signin/chrome_signin_client.cc b/chrome/browser/signin/chrome_signin_client.cc
index 22ee0f5..84a82ce 100644
--- a/chrome/browser/signin/chrome_signin_client.cc
+++ b/chrome/browser/signin/chrome_signin_client.cc
@@ -303,7 +303,8 @@
 
 void ChromeSigninClient::VerifySyncToken() {
 #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  if (signin_util::IsForceSigninEnabled())
+  // We only verifiy the token once when Profile is just created.
+  if (signin_util::IsForceSigninEnabled() && !force_signin_verifier_)
     force_signin_verifier_ = std::make_unique<ForceSigninVerifier>(profile_);
 #endif
 }
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 8af64b9a..496c28c6 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -51,6 +51,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/signin/account_management_screen_helper.h"
+#include "ui/android/view_android.h"
 #else
 #include "chrome/browser/ui/browser_commands.h"
 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
@@ -235,7 +236,8 @@
   } else {
     signin_metrics::LogAccountReconcilorStateOnGaiaResponse(
         account_reconcilor->GetState());
-    AccountManagementScreenHelper::OpenAccountManagementScreen(profile,
+    auto* window = web_contents->GetNativeView()->GetWindowAndroid();
+    AccountManagementScreenHelper::OpenAccountManagementScreen(window,
                                                                service_type);
   }
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index 51872bf..978acbda 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -33,8 +33,7 @@
     // User is signed in, but sync is disabled.
     return l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_DISABLED);
   }
-  if (service->HasDisableReason(
-          syncer::SyncService::DISABLE_REASON_USER_CHOICE)) {
+  if (!service->GetUserSettings()->IsSyncRequested()) {
     // User is signed in, but sync has been stopped.
     return l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED);
   }
@@ -49,26 +48,34 @@
           : IDS_SYNC_ACCOUNT_SYNCING_CUSTOM_DATA_TYPES);
 }
 
-void GetStatusForActionableError(syncer::ClientAction action,
+// Returns whether there is a non-empty status for the given |action|.
+bool GetStatusForActionableError(syncer::ClientAction action,
                                  base::string16* status_label,
                                  base::string16* link_label,
                                  ActionType* action_type) {
-  DCHECK(status_label);
-  DCHECK(link_label);
+  DCHECK(action_type);
   switch (action) {
     case syncer::UPGRADE_CLIENT:
-      *status_label = l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT);
-      *link_label =
-          l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT_LINK_LABEL);
+      if (status_label) {
+        *status_label = l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT);
+      }
+      if (link_label) {
+        *link_label =
+            l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT_LINK_LABEL);
+      }
       *action_type = UPGRADE_CLIENT;
-      break;
+      return true;
     case syncer::ENABLE_SYNC_ON_ACCOUNT:
-      *status_label =
-          l10n_util::GetStringUTF16(IDS_SYNC_STATUS_ENABLE_SYNC_ON_ACCOUNT);
-      break;
+      if (status_label) {
+        *status_label =
+            l10n_util::GetStringUTF16(IDS_SYNC_STATUS_ENABLE_SYNC_ON_ACCOUNT);
+      }
+      return true;
     default:
-      *status_label = base::string16();
-      break;
+      if (status_label) {
+        *status_label = base::string16();
+      }
+      return false;
   }
 }
 
@@ -80,8 +87,8 @@
   // Unrecoverable error is sometimes accompanied by actionable error.
   // If status message is set display that message, otherwise show generic
   // unrecoverable error message.
-  GetStatusForActionableError(action, status_label, link_label, action_type);
-  if (status_label->empty()) {
+  if (!GetStatusForActionableError(action, status_label, link_label,
+                                   action_type)) {
     *action_type = REAUTHENTICATE;
     *link_label = l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL);
 
@@ -148,6 +155,10 @@
     return PRE_SYNCED;
   }
 
+  // If local Sync were enabled, then the SyncService shouldn't report having a
+  // primary (or any) account.
+  DCHECK(!service->IsLocalSyncEnabled());
+
   syncer::SyncStatus status;
   service->QueryDetailedSyncStatus(&status);
 
@@ -164,10 +175,9 @@
   // TODO(crbug.com/911153): What's the intended meaning of this condition?
   // Should other disable reasons also be checked?
   if (service->GetUserSettings()->IsFirstSetupComplete() ||
+      !service->GetUserSettings()->IsSyncRequested() ||
       service->HasDisableReason(
-          syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY) ||
-      service->HasDisableReason(
-          syncer::SyncService::DISABLE_REASON_USER_CHOICE)) {
+          syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY)) {
     // The order or priority is going to be:
     // 1. Auth errors. 2. Actionable protocol errors. 3. Passphrase errors.
 
@@ -181,14 +191,9 @@
     }
 
     // We don't have an auth error. Check for an actionable protocol error.
-    // TODO(crbug.com/911153): Here the behavior (returned value) depends on
-    // whether the |status_label| param is null. That seems like a bug.
-    if (status_label && link_label) {
-      GetStatusForActionableError(status.sync_protocol_error.action,
-                                  status_label, link_label, action_type);
-      if (!status_label->empty()) {
-        return SYNC_ERROR;
-      }
+    if (GetStatusForActionableError(status.sync_protocol_error.action,
+                                    status_label, link_label, action_type)) {
+      return SYNC_ERROR;
     }
 
     // Check for a passphrase error.
@@ -209,8 +214,7 @@
 
     // Check to see if sync has been disabled via the dasboard and needs to be
     // set up once again.
-    if (service->HasDisableReason(
-            syncer::SyncService::DISABLE_REASON_USER_CHOICE) &&
+    if (!service->GetUserSettings()->IsSyncRequested() &&
         status.sync_protocol_error.error_type == syncer::NOT_MY_BIRTHDAY) {
       return PRE_SYNCED;
     }
@@ -236,27 +240,16 @@
     return PRE_SYNCED;
   }
 
-  if (ShouldRequestSyncConfirmation(service)) {
-    if (status_label && link_label) {
-      *status_label =
-          l10n_util::GetStringUTF16(IDS_SYNC_SETTINGS_NOT_CONFIRMED);
-      *link_label = l10n_util::GetStringUTF16(
-          IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON);
-    }
-    *action_type = CONFIRM_SYNC_SETTINGS;
-    return SYNC_ERROR;
+  // At this point we've ruled out all other cases - all that's left is a
+  // missing Sync confirmation.
+  DCHECK(ShouldRequestSyncConfirmation(service));
+  if (status_label && link_label) {
+    *status_label = l10n_util::GetStringUTF16(IDS_SYNC_SETTINGS_NOT_CONFIRMED);
+    *link_label = l10n_util::GetStringUTF16(
+        IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON);
   }
-
-  // TODO(crbug.com/906995): This code is only reachable if IsLocalSyncEnabled()
-  // is true. That should probably be handled more explicitly, and anyway this
-  // doesn't seem like an appropriate message for that case.
-  // The user is signed in, but sync has been stopped.
-  if (status_label) {
-    *status_label =
-        l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED);
-  }
-
-  return PRE_SYNCED;
+  *action_type = CONFIRM_SYNC_SETTINGS;
+  return SYNC_ERROR;
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/enable_disable_test.cc b/chrome/browser/sync/test/integration/enable_disable_test.cc
index 6c0c500a..46c20484 100644
--- a/chrome/browser/sync/test/integration/enable_disable_test.cc
+++ b/chrome/browser/sync/test/integration/enable_disable_test.cc
@@ -396,15 +396,28 @@
   // exact count.
   ASSERT_GT(GetNumUpdatesDownloadedInLastCycle(), 0);
 
-  // Stop and restart Sync.
+  // Stop Sync and let it start up again in standalone transport mode.
   GetClient(0)->StopSyncServiceWithoutClearingData();
+  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
+  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
+            GetSyncService(0)->GetTransportState());
+  ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureActive());
+
+  // Now start full Sync again.
+  base::HistogramTester histogram_tester;
   GetClient(0)->StartSyncService();
   ASSERT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
 
   // The bookmark should still be there, *without* having been redownloaded.
+  ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
       GURL(kSyncedBookmarkURL)));
-  EXPECT_EQ(0, GetNumUpdatesDownloadedInLastCycle());
+  EXPECT_EQ(
+      0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
+                                         /*REMOTE_NON_INITIAL_UPDATE=*/4));
+  EXPECT_EQ(
+      0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
+                                         /*REMOTE_INITIAL_UPDATE=*/5));
 }
 
 IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, ClearsPrefsIfClearData) {
@@ -456,57 +469,4 @@
   EXPECT_EQ(kTestServerChips, message.bag_of_chips().server_chips());
 }
 
-class EnableDisableSingleClientWithStandaloneTransportTest
-    : public EnableDisableSingleClientTest {
- public:
-  EnableDisableSingleClientWithStandaloneTransportTest() {
-    features_.InitAndEnableFeature(switches::kSyncStandaloneTransport);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientWithStandaloneTransportTest,
-                       DoesNotRedownloadAfterKeepDataWithStandaloneTransport) {
-  ASSERT_TRUE(SetupClients());
-  ASSERT_FALSE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
-      GURL(kSyncedBookmarkURL)));
-
-  // Create a bookmark on the server, then turn on Sync on the client.
-  InjectSyncedBookmark();
-  ASSERT_TRUE(GetClient(0)->SetupSync());
-  ASSERT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
-
-  // Make sure the bookmark got synced down.
-  ASSERT_TRUE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
-      GURL(kSyncedBookmarkURL)));
-  // Note: The response may also contain permanent nodes, so we can't check the
-  // exact count.
-  ASSERT_GT(GetNumUpdatesDownloadedInLastCycle(), 0);
-
-  // Stop Sync and let it start up again in standalone transport mode.
-  GetClient(0)->StopSyncServiceWithoutClearingData();
-  ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
-  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            GetSyncService(0)->GetTransportState());
-  ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureActive());
-
-  // Now start full Sync again.
-  base::HistogramTester histogram_tester;
-  GetClient(0)->StartSyncService();
-  ASSERT_TRUE(GetSyncService(0)->IsSyncFeatureActive());
-
-  // The bookmark should still be there, *without* having been redownloaded.
-  ASSERT_TRUE(SetupSync());
-  ASSERT_TRUE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
-      GURL(kSyncedBookmarkURL)));
-  EXPECT_EQ(
-      0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
-                                         /*REMOTE_NON_INITIAL_UPDATE=*/4));
-  EXPECT_EQ(
-      0, histogram_tester.GetBucketCount("Sync.ModelTypeEntityChange3.BOOKMARK",
-                                         /*REMOTE_INITIAL_UPDATE=*/5));
-}
-
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index 0059c984..5d3faa29 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -30,10 +30,7 @@
 class SingleClientSecondaryAccountSyncTest : public SyncTest {
  public:
   SingleClientSecondaryAccountSyncTest() : SyncTest(SINGLE_CLIENT) {
-    features_.InitWithFeatures(
-        /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                              switches::kSyncSupportSecondaryAccount},
-        /*disabled_features=*/{});
+    features_.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
   }
   ~SingleClientSecondaryAccountSyncTest() override {}
 
@@ -60,30 +57,6 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientSecondaryAccountSyncTest);
 };
 
-class SingleClientSecondaryAccountWithoutStandaloneTransportSyncTest
-    : public SingleClientSecondaryAccountSyncTest {
- public:
-  SingleClientSecondaryAccountWithoutStandaloneTransportSyncTest() {
-    features_.InitAndDisableFeature(switches::kSyncStandaloneTransport);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    SingleClientSecondaryAccountWithoutStandaloneTransportSyncTest,
-    DoesNotStartSyncWithStandaloneTransportDisabled) {
-  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
-
-  // Since standalone transport is disabled, just signing in (without making the
-  // account Chrome's primary one) should *not* start the Sync machinery.
-  secondary_account_helper::SignInSecondaryAccount(
-      profile(), &test_url_loader_factory_, "user@email.com");
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            GetSyncService(0)->GetTransportState());
-}
-
 class SingleClientSecondaryAccountWithoutSecondaryAccountSupportSyncTest
     : public SingleClientSecondaryAccountSyncTest {
  public:
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index dd8baab8..4553e441 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -42,39 +42,13 @@
 
 class SingleClientStandaloneTransportSyncTest : public SyncTest {
  public:
-  SingleClientStandaloneTransportSyncTest() : SyncTest(SINGLE_CLIENT) {
-    features_.InitAndEnableFeature(switches::kSyncStandaloneTransport);
-  }
+  SingleClientStandaloneTransportSyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientStandaloneTransportSyncTest() override {}
 
  private:
-  base::test::ScopedFeatureList features_;
-
   DISALLOW_COPY_AND_ASSIGN(SingleClientStandaloneTransportSyncTest);
 };
 
-class SingleClientStandaloneTransportFeatureDisabledSyncTest : public SyncTest {
- public:
-  SingleClientStandaloneTransportFeatureDisabledSyncTest()
-      : SyncTest(SINGLE_CLIENT) {
-    features_.InitAndDisableFeature(switches::kSyncStandaloneTransport);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(SingleClientStandaloneTransportFeatureDisabledSyncTest,
-                       DoesNotStartSyncWithFeatureDisabled) {
-  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
-
-  // Since standalone transport is disabled, signing in should *not* start the
-  // Sync machinery.
-  ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount());
-  EXPECT_EQ(syncer::SyncService::TransportState::WAITING_FOR_START_REQUEST,
-            GetSyncService(0)->GetTransportState());
-}
-
 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
 IN_PROC_BROWSER_TEST_F(SingleClientStandaloneTransportSyncTest,
                        StartsSyncFeatureOnSignin) {
diff --git a/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
index 45d86bd..4ff01a0d 100644
--- a/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
@@ -186,21 +186,10 @@
   EXPECT_TRUE(ExpectUserConsents({specifics1, specifics2}));
 }
 
-class SingleClientUserConsentsWithStandaloneTransportSyncTest
-    : public SingleClientUserConsentsSyncTest {
- public:
-  SingleClientUserConsentsWithStandaloneTransportSyncTest() {
-    features_.InitAndEnableFeature(switches::kSyncStandaloneTransport);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
 #if !defined(OS_CHROMEOS)
-IN_PROC_BROWSER_TEST_F(SingleClientUserConsentsWithStandaloneTransportSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientUserConsentsSyncTest,
                        ShouldSubmitIfSignedInAlthoughFullSyncNotEnabled) {
   // We avoid calling SetupSync(), because we don't want to turn on full sync,
   // only sign in such that the standalone transport starts.
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 491f6d3..fc6a58f 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -312,8 +312,7 @@
  public:
   SingleClientWalletWithAccountStorageSyncTest() {
     InitWithFeatures(
-        /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                              switches::kSyncUSSAutofillWalletData,
+        /*enabled_features=*/{switches::kSyncUSSAutofillWalletData,
                               autofill::features::
                                   kAutofillEnableAccountWalletStorage},
         /*disabled_features=*/{});
@@ -1092,8 +1091,7 @@
  public:
   SingleClientWalletSecondaryAccountSyncTest() {
     InitWithFeatures(
-        /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                              switches::kSyncSupportSecondaryAccount,
+        /*enabled_features=*/{switches::kSyncSupportSecondaryAccount,
                               switches::kSyncUSSAutofillWalletData,
                               autofill::features::
                                   kAutofillEnableAccountWalletStorage},
diff --git a/chrome/browser/ui/webui/certificate_viewer_webui.cc b/chrome/browser/ui/webui/certificate_viewer_webui.cc
index d0ece31..923fa172 100644
--- a/chrome/browser/ui/webui/certificate_viewer_webui.cc
+++ b/chrome/browser/ui/webui/certificate_viewer_webui.cc
@@ -15,6 +15,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/certificate_viewer.h"
@@ -204,14 +205,7 @@
   // Certificate usage.
   std::vector<std::string> usages;
   x509_certificate_model::GetUsageStrings(cert_hnd, &usages);
-  std::string usagestr;
-  for (auto it = usages.begin(); it != usages.end(); ++it) {
-    if (usagestr.length() > 0) {
-      usagestr += "\n";
-    }
-    usagestr += *it;
-  }
-  cert_info.SetString("general.usages", usagestr);
+  cert_info.SetString("general.usages", base::JoinString(usages, "\n"));
 
   // Standard certificate details.
   const std::string alternative_text =
@@ -260,25 +254,24 @@
       x509_certificate_model::HashCertSHA1(cert_hnd));
 
   // Certificate hierarchy is constructed from bottom up.
-  std::unique_ptr<base::ListValue> children;
+  base::Value children;
   int index = 0;
-  for (auto i = nss_certs_.begin(); i != nss_certs_.end(); ++i, ++index) {
-    std::unique_ptr<base::DictionaryValue> cert_node(
-        new base::DictionaryValue());
-    base::ListValue cert_details;
-    cert_node->SetString("label",
-                         x509_certificate_model::GetTitle(i->get()).c_str());
-    cert_node->SetDouble("payload.index", index);
+  for (const auto& cert : nss_certs_) {
+    base::Value cert_node(base::Value::Type::DICTIONARY);
+    cert_node.SetKey("label",
+                     base::Value(x509_certificate_model::GetTitle(cert.get())));
+    cert_node.SetPath({"payload", "index"}, base::Value(index));
     // Add the child from the previous iteration.
-    if (children)
-      cert_node->Set("children", std::move(children));
+    if (!children.is_none())
+      cert_node.SetKey("children", std::move(children));
 
     // Add this node to the children list for the next iteration.
-    children = std::make_unique<base::ListValue>();
-    children->Append(std::move(cert_node));
+    children = base::Value(base::Value::Type::LIST);
+    children.GetList().push_back(std::move(cert_node));
+    ++index;
   }
   // Set the last node as the top of the certificate hierarchy.
-  cert_info.Set("hierarchy", std::move(children));
+  cert_info.SetKey("hierarchy", std::move(children));
 
   base::JSONWriter::Write(cert_info, &data);
 
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
index 5d97e891..8b85f10 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_chromeos.cc
@@ -12,7 +12,6 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/webui/signin/inline_login_handler.h"
 #include "chromeos/account_manager/account_manager.h"
 #include "chromeos/account_manager/account_manager_factory.h"
@@ -33,15 +32,13 @@
 class SigninHelper : public GaiaAuthConsumer {
  public:
   SigninHelper(
-      Profile* profile,
       chromeos::AccountManager* account_manager,
       const base::RepeatingClosure& close_dialog_closure,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& gaia_id,
       const std::string& email,
       const std::string& auth_code)
-      : profile_(profile),
-        account_manager_(account_manager),
+      : account_manager_(account_manager),
         close_dialog_closure_(close_dialog_closure),
         email_(email),
         gaia_auth_fetcher_(this,
@@ -57,22 +54,18 @@
 
   // GaiaAuthConsumer overrides.
   void OnClientOAuthSuccess(const ClientOAuthResult& result) override {
-    // TODO(sinhak): Do not depend on Profile unnecessarily. A Profile should
-    // call |IdentityManagerFactory| for the list of accounts it wants to
-    // pull from |AccountManager|, not the other way round. Remove this when we
-    // release multi Profile on Chrome OS and have the infra in place to do
-    // this.
-    // Account info needs to be seeded before the OAuth2TokenService chain can
-    // use it. Do this before anything else.
-    AccountInfo account_info;
-    account_info.gaia = account_key_.id;
-    account_info.email = email_;
-    // TODO(crbug.com/922026): SigninHelper and InlineLoginHandlerChromeOS
-    // must be refactored to remove this use of LegacySeedAccountInfo. It should
-    // be possible to replace these two calls with a call to
-    // |AccountsMutator::AddOrUpdateAccount()|.
-    IdentityManagerFactory::GetForProfile(profile_)->LegacySeedAccountInfo(
-        account_info);
+    // Flow of control after this call:
+    // |AccountManager::UpsertAccount| updates / inserts the account and calls
+    // its |Observer|s, one of which is |ChromeOSOAuth2TokenServiceDelegate|.
+    // |ChromeOSOAuth2TokenServiceDelegate::OnTokenUpserted| seeds the Gaia id
+    // and email id for this account in |AccountTrackerService| and invokes
+    // |FireRefreshTokenAvailable|. This causes the account to propagate
+    // throughout the Identity Service chain, including in
+    // |AccountFetcherService|. |AccountFetcherService::OnRefreshTokenAvailable|
+    // invokes |AccountTrackerService::StartTrackingAccount|, triggers a fetch
+    // for the account information from Gaia and updates this information into
+    // |AccountTrackerService|. At this point the account will be fully added to
+    // the system.
     account_manager_->UpsertAccount(account_key_, email_, result.refresh_token);
 
     close_dialog_closure_.Run();
@@ -86,8 +79,6 @@
   }
 
  private:
-  // A non-owning pointer to Profile.
-  Profile* const profile_;
   // A non-owning pointer to Chrome OS AccountManager.
   chromeos::AccountManager* const account_manager_;
   // A closure to close the hosting dialog window.
@@ -147,7 +138,7 @@
           ->GetAccountManager(profile->GetPath().value());
 
   // SigninHelper deletes itself after its work is done.
-  new SigninHelper(profile, account_manager, close_dialog_closure_,
+  new SigninHelper(account_manager, close_dialog_closure_,
                    account_manager->GetUrlLoaderFactory(), gaia_id, email,
                    auth_code);
 }
diff --git a/chrome/browser/ui/webui/sync_internals_browsertest.js b/chrome/browser/ui/webui/sync_internals_browsertest.js
index 4c48fa8..11b09ea 100644
--- a/chrome/browser/ui/webui/sync_internals_browsertest.js
+++ b/chrome/browser/ui/webui/sync_internals_browsertest.js
@@ -61,22 +61,6 @@
   }
 };
 
-function SyncInternalsWebUITestWithStandaloneTransport() {}
-
-SyncInternalsWebUITestWithStandaloneTransport.prototype = {
-  __proto__: SyncInternalsWebUITest.prototype,
-
-  featureList: ['switches::kSyncStandaloneTransport', ''],
-};
-
-function SyncInternalsWebUITestWithoutStandaloneTransport() {}
-
-SyncInternalsWebUITestWithoutStandaloneTransport.prototype = {
-  __proto__: SyncInternalsWebUITest.prototype,
-
-  featureList: ['', 'switches::kSyncStandaloneTransport'],
-};
-
 /**
  * Constant hard-coded value to return from mock getAllNodes.
  * @const
diff --git a/chrome/browser/vr/text_perftest.cc b/chrome/browser/vr/text_perftest.cc
index 172d674..e24071e 100644
--- a/chrome/browser/vr/text_perftest.cc
+++ b/chrome/browser/vr/text_perftest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/strings/utf_string_conversions.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "chrome/browser/vr/elements/text.h"
 #include "chrome/browser/vr/skia_surface_provider_factory.h"
 #include "chrome/browser/vr/test/constants.h"
@@ -43,7 +43,7 @@
  protected:
   void PrintResults(const std::string& name) {
     perf_test::PrintResult("TextPerfTest", ".render_time_avg", name,
-                           timer_.MsPerLap(), "ms", true);
+                           timer_.TimePerLap().InMillisecondsF(), "ms", true);
     perf_test::PrintResult("TextPerfTest", ".number_of_runs", name,
                            static_cast<size_t>(timer_.NumLaps()), "runs", true);
   }
@@ -57,7 +57,7 @@
   }
 
   std::unique_ptr<Text> text_element_;
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
 
  private:
   std::unique_ptr<SkiaSurfaceProvider> provider_;
diff --git a/chrome/common/extensions/docs/templates/intros/browsingData.html b/chrome/common/extensions/docs/templates/intros/browsingData.html
index d8bf735..0489daa 100644
--- a/chrome/common/extensions/docs/templates/intros/browsingData.html
+++ b/chrome/common/extensions/docs/templates/intros/browsingData.html
@@ -91,6 +91,37 @@
   to keep your users up to date on the removal's status.
 </p>
 
+
+<h2 id="specific_origins">Specific Origins</h2>
+
+<p>To remove data for a specific origin or to exclude a set of origins from
+deletion, you can use the <code>RemovalOptions.origins</code> and
+<code>RemovalOptions.excludeOrigins</code> parameters. They can only be applied
+to cookies, cache, and storage (CacheStorage, FileSystems, IndexedDB,
+LocalStorage, PluginData, ServiceWorkers, and WebSQL).
+</p>
+
+<pre>chrome.browsingData.remove({
+  "origins": ["https://www.example.com"]
+}, {
+  "cacheStorage": true,
+  "cookies": true,
+  "fileSystems": true,
+  "indexedDB": true,
+  "localStorage": true,
+  "pluginData": true,
+  "serviceWorkers": true,
+  "webSQL": true
+}, callback);</pre>
+
+<p class="caution">
+    <strong>Important</strong>: As cookies are scoped more broadly than other
+    types of storage, deleting cookies for an origin will delete all cookies of
+    the registrable domain. For example, deleting data for
+    <code>https://www.example.com</code> will delete cookies with a domain of
+    <code>.example.com</code> as well.
+</p>
+
 <h2 id="origin_types">Origin Types</h2>
 
 <p>
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.cc b/chrome/credential_provider/gaiacp/associated_user_validator.cc
index fc6f523..44a9a9bd 100644
--- a/chrome/credential_provider/gaiacp/associated_user_validator.cc
+++ b/chrome/credential_provider/gaiacp/associated_user_validator.cc
@@ -122,39 +122,6 @@
   return token_handle_validity;
 }
 
-HRESULT CleanupStaleUsersAndGetTokenHandles(
-    std::map<base::string16, base::string16>* sid_to_handle) {
-  DCHECK(sid_to_handle);
-  std::map<base::string16, UserTokenHandleInfo> sids_to_handle_info;
-
-  HRESULT hr = GetUserTokenHandles(&sids_to_handle_info);
-  if (FAILED(hr)) {
-    LOGFN(ERROR) << "GetUserAssociationInfo hr=" << putHR(hr);
-    return hr;
-  }
-
-  OSUserManager* manager = OSUserManager::Get();
-  for (const auto& sid_to_association : sids_to_handle_info) {
-    const base::string16& sid = sid_to_association.first;
-    const UserTokenHandleInfo& info = sid_to_association.second;
-    if (info.gaia_id.empty()) {
-      RemoveAllUserProperties(sid);
-      continue;
-    }
-    HRESULT hr = manager->FindUserBySID(sid.c_str(), nullptr, 0, nullptr, 0);
-    if (hr == HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)) {
-      RemoveAllUserProperties(sid);
-      continue;
-    } else if (FAILED(hr)) {
-      LOGFN(ERROR) << "manager->FindUserBySID hr=" << putHR(hr);
-    }
-
-    sid_to_handle->emplace(sid, info.token_handle);
-  }
-
-  return S_OK;
-}
-
 HRESULT ModifyUserAccess(const std::unique_ptr<ScopedLsaPolicy>& policy,
                          const base::string16& sid,
                          bool allow) {
@@ -242,13 +209,53 @@
   return InternetAvailabilityChecker::Get()->HasInternetConnection();
 }
 
-void AssociatedUserValidator::GetAssociatedSids(
-    std::set<base::string16>* associated_sids) {
-  DCHECK(associated_sids);
+HRESULT AssociatedUserValidator::UpdateAssociatedSids(
+    std::map<base::string16, base::string16>* sid_to_handle) {
+  std::map<base::string16, UserTokenHandleInfo> sids_to_handle_info;
 
-  associated_sids->clear();
+  HRESULT hr = GetUserTokenHandles(&sids_to_handle_info);
+  if (FAILED(hr)) {
+    LOGFN(ERROR) << "GetUserTokenHandles hr=" << putHR(hr);
+    return hr;
+  }
+
+  std::set<base::string16> users_to_delete;
+  OSUserManager* manager = OSUserManager::Get();
+  for (const auto& sid_to_association : sids_to_handle_info) {
+    const base::string16& sid = sid_to_association.first;
+    const UserTokenHandleInfo& info = sid_to_association.second;
+    if (info.gaia_id.empty()) {
+      users_to_delete.insert(sid_to_association.first);
+      continue;
+    }
+    HRESULT hr = manager->FindUserBySID(sid.c_str(), nullptr, 0, nullptr, 0);
+    if (hr == HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)) {
+      users_to_delete.insert(sid_to_association.first);
+      continue;
+    } else if (FAILED(hr)) {
+      LOGFN(ERROR) << "manager->FindUserBySID hr=" << putHR(hr);
+    }
+
+    if (sid_to_handle)
+      sid_to_handle->emplace(sid, info.token_handle);
+  }
+
+  for (const auto& to_delete : users_to_delete) {
+    user_to_token_handle_info_.erase(to_delete);
+    RemoveAllUserProperties(to_delete);
+  }
+
+  return S_OK;
+}
+
+std::set<base::string16> AssociatedUserValidator::GetUpdatedAssociatedSids() {
+  UpdateAssociatedSids(nullptr);
+
+  std::set<base::string16> associated_sids;
   for (const auto& it : user_to_token_handle_info_)
-    associated_sids->insert(it.first);
+    associated_sids.insert(it.first);
+
+  return associated_sids;
 }
 
 bool AssociatedUserValidator::IsUserAccessBlockingEnforced(
@@ -270,18 +277,16 @@
   if (!IsUserAccessBlockingEnforced(cpus))
     return;
 
-  std::map<base::string16, UserTokenHandleInfo> sids_to_association;
-
-  HRESULT hr = GetUserTokenHandles(&sids_to_association);
+  HRESULT hr = UpdateAssociatedSids(nullptr);
   if (FAILED(hr)) {
-    LOGFN(ERROR) << "GetUserAssociationInfo hr=" << putHR(hr);
+    LOGFN(ERROR) << "GetUserTokenHandles hr=" << putHR(hr);
     return;
   }
 
   auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS);
 
-  for (const auto& sid_to_association : sids_to_association) {
-    const base::string16& sid = sid_to_association.first;
+  for (const auto& user_info : user_to_token_handle_info_) {
+    const base::string16& sid = user_info.first;
     if (locked_user_sids_.find(sid) != locked_user_sids_.end())
       continue;
 
@@ -319,10 +324,10 @@
 
 void AssociatedUserValidator::StartRefreshingTokenHandleValidity() {
   std::map<base::string16, base::string16> sid_to_handle;
-  HRESULT hr = CleanupStaleUsersAndGetTokenHandles(&sid_to_handle);
+  HRESULT hr = UpdateAssociatedSids(&sid_to_handle);
 
   if (FAILED(hr)) {
-    LOGFN(ERROR) << "CleanupStaleUsersAndGetTokenHandles hr=" << putHR(hr);
+    LOGFN(ERROR) << "UpdateAssociatedSids hr=" << putHR(hr);
     return;
   }
 
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.h b/chrome/credential_provider/gaiacp/associated_user_validator.h
index 070122e..7818c1f 100644
--- a/chrome/credential_provider/gaiacp/associated_user_validator.h
+++ b/chrome/credential_provider/gaiacp/associated_user_validator.h
@@ -68,7 +68,8 @@
 
   // Fills |associated_sids| with the sids of all valid associated users
   // found on this system.
-  void GetAssociatedSids(std::set<base::string16>* associated_sids);
+  std::set<base::string16> GetUpdatedAssociatedSids();
+  size_t GetAssociatedUsersCount() { return GetUpdatedAssociatedSids().size(); }
 
  protected:
   explicit AssociatedUserValidator(base::TimeDelta validation_timeout);
@@ -80,6 +81,8 @@
   void StartTokenValidityQuery(const base::string16& sid,
                                const base::string16& token_handle,
                                base::TimeDelta timeout);
+  HRESULT UpdateAssociatedSids(
+      std::map<base::string16, base::string16>* sid_to_handle);
 
   // Returns the storage used for the instance pointer.
   static AssociatedUserValidator** GetInstanceStorage();
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index abee88b9..3498201 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -32,6 +32,7 @@
 #include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/associated_user_validator.h"
 #include "chrome/credential_provider/gaiacp/auth_utils.h"
+#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
 #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
 #include "chrome/credential_provider/gaiacp/gaia_resources.h"
 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
@@ -1573,11 +1574,18 @@
 
   // New users creation is not allowed during work station unlock. This code
   // prevents users from being created when the "Other User" tile appears on the
-  // lock screen through a combination of system policy. In this situation only
+  // lock screen through certain system policy settings. In this situation only
   // the user who locked the computer is allowed to sign in.
   if (cpus == CPUS_UNLOCK_WORKSTATION) {
     *error_text = AllocErrorString(IDS_INVALID_UNLOCK_WORKSTATION_USER_BASE);
     return HRESULT_FROM_WIN32(ERROR_LOGON_TYPE_NOT_GRANTED);
+    // This code prevents users from being created when the "Other User" tile
+    // appears on the sign in scenario and only 1 user is allowed to use this
+    // system.
+  } else if (!CGaiaCredentialProvider::CanNewUsersBeCreated(
+                 static_cast<CREDENTIAL_PROVIDER_USAGE_SCENARIO>(cpus))) {
+    *error_text = AllocErrorString(IDS_ADD_USER_DISALLOWED_BASE);
+    return HRESULT_FROM_WIN32(ERROR_LOGON_TYPE_NOT_GRANTED);
   }
 
   base::string16 local_fullname = GetDictString(result, kKeyFullname);
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
index 84210d0..90aebea 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -543,6 +543,52 @@
       GetStringResource(IDS_INVALID_UNLOCK_WORKSTATION_USER_BASE).c_str());
 }
 
+TEST_F(GcpGaiaCredentialBaseTest, NewUserDisabledThroughMdm) {
+  USES_CONVERSION;
+  FakeAssociatedUserValidator validator;
+  FakeInternetAvailabilityChecker internet_checker;
+
+  // Enforce single user mode for MDM.
+  ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com"));
+  ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 0));
+  GoogleMdmEnrolledStatusForTesting force_success(true);
+
+  // Create a fake user that is already associated so when the user tries to
+  // sign on and create a new user, it fails.
+  CComBSTR sid;
+  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                      L"foo_registered", L"password", L"name", L"comment",
+                      L"gaia-id-registered", base::string16(), &sid));
+
+  FakeGaiaCredentialProvider provider;
+
+  // Populate the associated users list, token handle validity does not matter
+  // in this test.
+  validator.StartRefreshingTokenHandleValidity();
+
+  // Start logon.
+  CComPtr<IGaiaCredential> gaia_cred;
+  CComPtr<ICredentialProviderCredential> cred;
+  ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
+
+  CComPtr<ITestCredential> test;
+  ASSERT_EQ(S_OK, cred.QueryInterface(&test));
+
+  ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
+
+  ASSERT_EQ(S_OK, gaia_cred->Terminate());
+
+  // Check that values were not propagated to the provider.
+  EXPECT_EQ(0u, provider.username().Length());
+  EXPECT_EQ(0u, provider.password().Length());
+  EXPECT_EQ(0u, provider.sid().Length());
+  EXPECT_EQ(FALSE, provider.credentials_changed_fired());
+
+  // Sign in should fail with an error stating that no new users can be created.
+  ASSERT_STREQ(test->GetErrorText(),
+               GetStringResource(IDS_ADD_USER_DISALLOWED_BASE).c_str());
+}
+
 TEST_F(GcpGaiaCredentialBaseTest, InvalidUserUnlockedAfterSignin) {
   // Enforce token handle verification with user locking when the token handle
   // is not valid.
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
index 5838c6e..03946f0 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
@@ -112,29 +112,6 @@
   AssociatedUserValidator::Get()->AllowSigninForUsersWithInvalidTokenHandles();
 }
 
-bool CGaiaCredentialProvider::ShouldCreateAnonymousCredential() {
-  // When in an unlock usage, only the user that locked the computer will be
-  // allowed to sign in, so no new users can be added.
-  if (cpus_ == CPUS_UNLOCK_WORKSTATION)
-    return false;
-
-  // If MDM enrollment is required and multiple users is not supported, only
-  // create the Gaia credential if there does not yet exist an OS user created
-  // from a Google account.  Otherwise we always create the Gaia credential.
-  if (MdmEnrollmentEnabled()) {
-    DWORD multi_user_supported = 0;
-    HRESULT hr = GetGlobalFlag(kRegMdmSupportsMultiUser, &multi_user_supported);
-    if (FAILED(hr) || multi_user_supported == 0) {
-      DWORD user_count;
-      hr = GetUserCount(&user_count);
-      if (SUCCEEDED(hr) && user_count > 0)
-        return false;
-    }
-  }
-
-  return true;
-}
-
 bool CGaiaCredentialProvider::ShouldCreateAnonymousReauthCredential(
     bool other_user_credential_exists) {
   // If user lockout is not enforced, no need to create anonymous reauth
@@ -197,7 +174,7 @@
   if (showing_other_user) {
     hr = CComCreator<CComObject<COtherUserGaiaCredential>>::CreateInstance(
         nullptr, IID_IGaiaCredential, reinterpret_cast<void**>(&cred));
-  } else if (ShouldCreateAnonymousCredential()) {
+  } else if (CanNewUsersBeCreated(cpus_)) {
     hr = CComCreator<CComObject<CGaiaCredential>>::CreateInstance(
         nullptr, IID_IGaiaCredential, reinterpret_cast<void**>(&cred));
   } else {
@@ -308,8 +285,8 @@
   if (!ShouldCreateAnonymousReauthCredential(showing_other_user))
     return S_OK;
 
-  std::set<base::string16> associated_sids;
-  AssociatedUserValidator::Get()->GetAssociatedSids(&associated_sids);
+  std::set<base::string16> associated_sids =
+      AssociatedUserValidator::Get()->GetUpdatedAssociatedSids();
 
   OSUserManager* manager = OSUserManager::Get();
 
@@ -368,6 +345,29 @@
   return cpus == CPUS_LOGON || cpus == CPUS_UNLOCK_WORKSTATION;
 }
 
+// Static.
+bool CGaiaCredentialProvider::CanNewUsersBeCreated(
+    CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) {
+  // When in an unlock usage, only the user that locked the computer will be
+  // allowed to sign in, so no new users can be added.
+  if (cpus == CPUS_UNLOCK_WORKSTATION)
+    return false;
+
+  // If MDM enrollment is required and multiple users is not supported, only
+  // allow a new associated user to be created if there does not yet exist an
+  // OS user created from a Google account.
+  if (MdmEnrollmentEnabled()) {
+    DWORD multi_user_supported = 0;
+    HRESULT hr = GetGlobalFlag(kRegMdmSupportsMultiUser, &multi_user_supported);
+    if (FAILED(hr) || multi_user_supported == 0) {
+      if (AssociatedUserValidator::Get()->GetAssociatedUsersCount() > 0)
+        return false;
+    }
+  }
+
+  return true;
+}
+
 // IGaiaCredentialProvider ////////////////////////////////////////////////////
 
 HRESULT CGaiaCredentialProvider::GetUsageScenario(DWORD* cpus) {
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.h b/chrome/credential_provider/gaiacp/gaia_credential_provider.h
index 0e601a23..3da35afa4 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider.h
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.h
@@ -43,12 +43,16 @@
   HRESULT FinalConstruct();
   void FinalRelease();
 
+  // Returns true if the given usage scenario is supported by GCPW. Currently
+  // only CPUS_LOGON and CPUS_UNLOCK_WORKSTATION are supported.
   static bool IsUsageScenarioSupported(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus);
 
- private:
-  // Checks whether an anonymous credential (CGaiaCredential) should be created.
-  bool ShouldCreateAnonymousCredential();
+  // Returns true if a new user can be added in the current usage scenario. This
+  // function also checks other settings controlled by registry settings to
+  // determine the result of this query.
+  static bool CanNewUsersBeCreated(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus);
 
+ private:
   // Checks whether anonymous reauth credentials should be created given the
   // usage scenario and also whether an "Other User" tile exists on the sign on
   // screen.
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
index 147f828..f6d236d 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
@@ -181,6 +181,7 @@
   // exists.
   ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com"));
   ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 0));
+  GoogleMdmEnrolledStatusForTesting forced_status(true);
 
   const wchar_t kDummyUsername[] = L"username";
   const wchar_t kDummyPassword[] = L"password";
@@ -188,6 +189,9 @@
   CreateGCPWUser(kDummyUsername, L"foo@gmail.com", kDummyPassword, L"Full Name",
                  L"Comment", L"gaia-id", &sid);
 
+  // Start token handle refresh threads.
+  associated_user_validator.StartRefreshingTokenHandleValidity();
+
   {
     CComPtr<ICredentialProvider> provider;
     ASSERT_EQ(S_OK,
@@ -216,8 +220,6 @@
   ASSERT_EQ(S_OK,
             fake_os_user_manager()->RemoveUser(kDummyUsername, kDummyPassword));
 
-  // Start token handle refresh threads.
-  associated_user_validator.StartRefreshingTokenHandleValidity();
   {
     CComPtr<ICredentialProvider> provider;
     ASSERT_EQ(S_OK,
@@ -263,8 +265,13 @@
   const DWORD expected_credential_count =
       config_mdm_url && supports_multi_users != 1 && user_exists ? 0 : 1;
 
-  if (config_mdm_url)
+  bool mdm_enrolled = false;
+  if (config_mdm_url) {
     ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com"));
+    mdm_enrolled = true;
+  }
+
+  GoogleMdmEnrolledStatusForTesting forced_status(mdm_enrolled);
 
   if (supports_multi_users != 2) {
     ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser,
@@ -282,6 +289,8 @@
       GURL(AssociatedUserValidator::kTokenInfoUrl),
       FakeWinHttpUrlFetcher::Headers(), "{\"expires_in\":1}");
 
+  associated_user_validator.StartRefreshingTokenHandleValidity();
+
   CComPtr<ICredentialProvider> provider;
   ASSERT_EQ(S_OK,
             CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance(
diff --git a/chrome/credential_provider/gaiacp/gaia_resources.grd b/chrome/credential_provider/gaiacp/gaia_resources.grd
index 4d6932ec..a18c84c 100644
--- a/chrome/credential_provider/gaiacp/gaia_resources.grd
+++ b/chrome/credential_provider/gaiacp/gaia_resources.grd
@@ -115,6 +115,9 @@
       <message name="IDS_INVALID_UNLOCK_WORKSTATION_USER" desc="">
         Only the user that locked this workstation is currently allowed sign in to the system.
       </message>
+      <message name="IDS_ADD_USER_DISALLOWED" desc="">
+        Failed to create new registered Google user. This system only allows one registered Google user to be created.
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/credential_provider/gaiacp/gaia_resources_grd/IDS_ADD_USER_DISALLOWED.png.sha1 b/chrome/credential_provider/gaiacp/gaia_resources_grd/IDS_ADD_USER_DISALLOWED.png.sha1
new file mode 100644
index 0000000..2041fe7
--- /dev/null
+++ b/chrome/credential_provider/gaiacp/gaia_resources_grd/IDS_ADD_USER_DISALLOWED.png.sha1
@@ -0,0 +1 @@
+7df62d83b544821330f561da0039a33f586ae389
\ No newline at end of file
diff --git a/chrome/credential_provider/gaiacp/reg_utils.cc b/chrome/credential_provider/gaiacp/reg_utils.cc
index d92b0ee..e65dcaa 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.cc
+++ b/chrome/credential_provider/gaiacp/reg_utils.cc
@@ -177,17 +177,6 @@
   return SetMachineRegDWORD(kGcpRootKeyName, name, value);
 }
 
-HRESULT GetUserCount(DWORD* count) {
-  DCHECK(count);
-
-  wchar_t key_name[128];
-  swprintf_s(key_name, base::size(key_name), L"%s\\Users", kGcpRootKeyName);
-
-  base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, key_name);
-  *count = iter.SubkeyCount();
-  return S_OK;
-}
-
 HRESULT GetUserProperty(const base::string16& sid,
                         const base::string16& name,
                         DWORD* value) {
diff --git a/chrome/credential_provider/gaiacp/reg_utils.h b/chrome/credential_provider/gaiacp/reg_utils.h
index c73d106..641ab377 100644
--- a/chrome/credential_provider/gaiacp/reg_utils.h
+++ b/chrome/credential_provider/gaiacp/reg_utils.h
@@ -51,9 +51,6 @@
                                 const base::string16& value);
 HRESULT SetGlobalFlagForTesting(const base::string16& name, DWORD value);
 
-// Gets the number of users assoicated with this credential provider.
-HRESULT GetUserCount(DWORD* count);
-
 // Gets DWORD property set for the given user.
 HRESULT GetUserProperty(const base::string16& sid,
                         const base::string16& name,
diff --git a/chrome/test/chromedriver/net/websocket.cc b/chrome/test/chromedriver/net/websocket.cc
index 1eacaf3..e5f7df07 100644
--- a/chrome/test/chromedriver/net/websocket.cc
+++ b/chrome/test/chromedriver/net/websocket.cc
@@ -246,9 +246,9 @@
 void WebSocket::OnReadDuringHandshake(const char* data, int len) {
   VLOG(4) << "WebSocket::OnReadDuringHandshake\n" << std::string(data, len);
   handshake_response_ += std::string(data, len);
-  int headers_end = net::HttpUtil::LocateEndOfHeaders(
+  size_t headers_end = net::HttpUtil::LocateEndOfHeaders(
       handshake_response_.data(), handshake_response_.size(), 0);
-  if (headers_end == -1)
+  if (headers_end == std::string::npos)
     return;
 
   const char kMagicKey[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
diff --git a/components/autofill/core/browser/phone_number_i18n_unittest.cc b/components/autofill/core/browser/phone_number_i18n_unittest.cc
index 16269c3c..a65481ec 100644
--- a/components/autofill/core/browser/phone_number_i18n_unittest.cc
+++ b/components/autofill/core/browser/phone_number_i18n_unittest.cc
@@ -272,6 +272,37 @@
             i18n::FormatPhoneForResponse("(1) 515-123-1234", "US"));
 }
 
+// Tests that phone numbers are correctly formatted in a national format.
+TEST(PhoneNumberUtilTest, FormatPhoneNationallyForDisplay) {
+  // Invalid US and Brazilian numbers are not formatted.
+  EXPECT_EQ("1234567890",
+            i18n::FormatPhoneNationallyForDisplay("1234567890", "US"));
+  EXPECT_EQ("(11) 13333-4444",
+            i18n::FormatPhoneNationallyForDisplay("(11) 13333-4444", "BR"));
+  EXPECT_EQ("(11) 13333-4444",
+            i18n::FormatPhoneNationallyForDisplay("(11) 13333-4444", "IN"));
+
+  // Valid US, Canadian, UK, and Brazilian numbers are nationally formatted.
+  EXPECT_EQ("(202) 444-0000",
+            i18n::FormatPhoneNationallyForDisplay("2024440000", "US"));
+  EXPECT_EQ("(202) 444-0000",
+            i18n::FormatPhoneNationallyForDisplay("+1(202)4440000", "US"));
+  EXPECT_EQ("(202) 444-0000",
+            i18n::FormatPhoneNationallyForDisplay("12024440000", "US"));
+  EXPECT_EQ("(202) 444-0000",
+            i18n::FormatPhoneNationallyForDisplay("(202)4440000", "US"));
+  EXPECT_EQ("(202) 444-0000",
+            i18n::FormatPhoneNationallyForDisplay("202-444-0000", "US"));
+  EXPECT_EQ("(819) 555-9999",
+            i18n::FormatPhoneNationallyForDisplay("+1(819)555 9999", "CA"));
+  EXPECT_EQ("(819) 555-9999",
+            i18n::FormatPhoneNationallyForDisplay("18195559999", "CA"));
+  EXPECT_EQ("020 7601 4444",
+            i18n::FormatPhoneNationallyForDisplay("+4402076014444", "UK"));
+  EXPECT_EQ("(21) 3883-5600",
+            i18n::FormatPhoneNationallyForDisplay("2138835600", "BR"));
+}
+
 // Tests that the phone numbers are correctly formatted to display to the user.
 TEST(PhoneNumberUtilTest, FormatPhoneForDisplay) {
   // Invalid number is not formatted.
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
index 98721d9..5f511af4a 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.cc
@@ -320,7 +320,9 @@
   LoadDataCacheAndMetadata();
 }
 
-AutofillWalletMetadataSyncBridge::~AutofillWalletMetadataSyncBridge() {}
+AutofillWalletMetadataSyncBridge::~AutofillWalletMetadataSyncBridge() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
 void AutofillWalletMetadataSyncBridge::OnWalletDataTrackingStateChanged(
     bool is_tracking) {
@@ -338,6 +340,7 @@
 AutofillWalletMetadataSyncBridge::MergeSyncData(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // First upload local entities that are not mentioned in |entity_data|.
   UploadInitialLocalData(metadata_change_list.get(), entity_data);
 
@@ -349,6 +352,7 @@
 AutofillWalletMetadataSyncBridge::ApplySyncChanges(
     std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
     syncer::EntityChangeList entity_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return MergeRemoteChanges(std::move(metadata_change_list),
                             std::move(entity_data));
 }
@@ -369,6 +373,7 @@
 
 std::string AutofillWalletMetadataSyncBridge::GetClientTag(
     const syncer::EntityData& entity_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   const WalletMetadataSpecifics& remote_metadata =
       entity_data.specifics.wallet_metadata();
   return GetClientTagForSpecificsId(remote_metadata.type(),
@@ -377,6 +382,7 @@
 
 std::string AutofillWalletMetadataSyncBridge::GetStorageKey(
     const syncer::EntityData& entity_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return GetStorageKeyForWalletMetadataTypeAndSpecificsId(
       entity_data.specifics.wallet_metadata().type(),
       entity_data.specifics.wallet_metadata().id());
diff --git a/components/autofill_assistant/browser/details.cc b/components/autofill_assistant/browser/details.cc
index 9cef1232..b231dada 100644
--- a/components/autofill_assistant/browser/details.cc
+++ b/components/autofill_assistant/browser/details.cc
@@ -63,6 +63,10 @@
 
 bool Details::UpdateFromParameters(
     const std::map<std::string, std::string>& parameters) {
+  // Whenever details are updated from parameters we want to animate missing
+  // data.
+  proto_.mutable_details()->set_animate_placeholders(true);
+  proto_.mutable_details()->set_show_image_placeholder(true);
   if (MaybeUpdateFromDetailsParameters(parameters)) {
     return true;
   }
@@ -136,7 +140,6 @@
       continue;
     }
   }
-
   return details_updated;
 }
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 46d80c2..057da29c 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -707,7 +707,11 @@
 message DetailsProto {
   optional string title = 1;
 
-  optional string image_url = 2;
+  oneof image {
+    string image_url = 2;
+    // When set to true shows placeholder in place of an image.
+    bool show_image_placeholder = 10;
+  }
 
   // Optional label to provide additional price information.
   optional string total_price_label = 9;
@@ -723,6 +727,15 @@
   optional DateTimeProto datetime = 3;
   optional string description = 4;
 
+  // Asks the UI to show animated placeholders for missing fields.
+  // The placeholder will be shown on effectively missing:
+  // * title
+  // * image
+  // * description line (1 or 2)
+  // TODO(crbug.com/806868): Make the fields for displaying placeholders
+  // configurable by the server.
+  optional bool animate_placeholders = 11;
+
   // Deprecated, no longer supported.
   reserved 5;
 }
diff --git a/components/browser_sync/browser_sync_switches.cc b/components/browser_sync/browser_sync_switches.cc
index 39d8763b..35e898c 100644
--- a/components/browser_sync/browser_sync_switches.cc
+++ b/components/browser_sync/browser_sync_switches.cc
@@ -26,6 +26,11 @@
 // flag is present.
 const char kLocalSyncBackendDir[] = "local-sync-backend-dir";
 
+// If enabled, the sync engine will be shut down in the "paused" state.
+// TODO(crbug.com/938819): Remove this after M74 has fully rolled out.
+const base::Feature kStopSyncInPausedState{"StopSyncInPausedState",
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
+
 bool IsSyncAllowedByFlag() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kDisableSync);
diff --git a/components/browser_sync/browser_sync_switches.h b/components/browser_sync/browser_sync_switches.h
index de0323c..8722853 100644
--- a/components/browser_sync/browser_sync_switches.h
+++ b/components/browser_sync/browser_sync_switches.h
@@ -7,6 +7,8 @@
 #ifndef COMPONENTS_BROWSER_SYNC_BROWSER_SYNC_SWITCHES_H_
 #define COMPONENTS_BROWSER_SYNC_BROWSER_SYNC_SWITCHES_H_
 
+#include "base/feature_list.h"
+
 namespace switches {
 
 extern const char kDisableSync[];
@@ -14,6 +16,8 @@
 extern const char kEnableLocalSyncBackend[];
 extern const char kLocalSyncBackendDir[];
 
+extern const base::Feature kStopSyncInPausedState;
+
 // Returns whether sync is allowed to run based on command-line switches.
 // Profile::IsSyncAllowed() is probably a better signal than this function.
 // This function can be called from any thread, and the implementation doesn't
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 8aaf8a7..04e4836 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -134,10 +134,6 @@
   return type_map;
 }
 
-bool IsStandaloneTransportEnabled() {
-  return base::FeatureList::IsEnabled(switches::kSyncStandaloneTransport);
-}
-
 }  // namespace
 
 ProfileSyncService::InitParams::InitParams() = default;
@@ -206,7 +202,7 @@
   startup_controller_ = std::make_unique<syncer::StartupController>(
       base::BindRepeating(&ProfileSyncService::GetPreferredDataTypes,
                           base::Unretained(this)),
-      base::BindRepeating(&ProfileSyncService::ShouldStartEngine,
+      base::BindRepeating(&ProfileSyncService::IsEngineAllowedToStart,
                           base::Unretained(this)),
       base::BindRepeating(&ProfileSyncService::StartUpSlowEngineComponents,
                           base::Unretained(this)));
@@ -375,34 +371,12 @@
 }
 
 bool ProfileSyncService::IsEngineAllowedToStart() const {
-  int disable_reasons = GetDisableReasons();
-  if (IsStandaloneTransportEnabled()) {
-    // USER_CHOICE (i.e. the Sync feature toggle) and PLATFORM_OVERRIDE (i.e.
-    // Android's "MasterSync" toggle) do not prevent starting up the Sync
-    // transport.
-    const int kDisableReasonMask =
-        ~(DISABLE_REASON_USER_CHOICE | DISABLE_REASON_PLATFORM_OVERRIDE);
-    disable_reasons &= kDisableReasonMask;
-  }
-  return disable_reasons == DISABLE_REASON_NONE;
-}
-
-bool ProfileSyncService::ShouldStartEngine(
-    bool bypass_first_setup_check) const {
-  if (!IsEngineAllowedToStart()) {
-    return false;
-  }
-  // If standalone transport is enabled, we always start the engine as soon as
-  // we can.
-  if (IsStandaloneTransportEnabled()) {
-    return true;
-  }
-  // Without standalone transport, we generally wait for first-time setup to be
-  // complete before starting the engine (because if it isn't, we can't
-  // configure the DataTypeManager anyway). Note that if a setup is currently in
-  // progress (which requires the engine to be initialized), then
-  // |bypass_first_setup_check| will be set to true.
-  return bypass_first_setup_check || IsFirstSetupComplete();
+  // USER_CHOICE (i.e. the Sync feature toggle) and PLATFORM_OVERRIDE (i.e.
+  // Android's "MasterSync" toggle) do not prevent starting up the Sync
+  // transport.
+  const int kDisableReasonMask =
+      ~(DISABLE_REASON_USER_CHOICE | DISABLE_REASON_PLATFORM_OVERRIDE);
+  return (GetDisableReasons() & kDisableReasonMask) == DISABLE_REASON_NONE;
 }
 
 bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
@@ -707,8 +681,10 @@
   if (unrecoverable_error_reason_ != ERROR_REASON_UNSET) {
     result = result | DISABLE_REASON_UNRECOVERABLE_ERROR;
   }
-  if (auth_manager_->IsSyncPaused()) {
-    result = result | DISABLE_REASON_PAUSED;
+  if (base::FeatureList::IsEnabled(switches::kStopSyncInPausedState)) {
+    if (auth_manager_->IsSyncPaused()) {
+      result = result | DISABLE_REASON_PAUSED;
+    }
   }
   return result;
 }
@@ -751,11 +727,6 @@
     return TransportState::PENDING_DESIRED_CONFIGURATION;
   }
 
-  // Unless standalone transport is enabled, the DataTypeManager shouldn't get
-  // configured (i.e. leave the STOPPED state) before the initial setup is
-  // complete.
-  DCHECK(IsStandaloneTransportEnabled() || IsFirstSetupComplete());
-
   // Note that if a setup is started after the data types have been configured,
   // then they'll stay configured even though CanConfigureDataTypes will be
   // false.
@@ -1196,7 +1167,6 @@
   // easier to keep it like this. Changing this will likely require changes to
   // the setup UI flow.
   return data_type_manager_ &&
-         (IsFirstSetupComplete() || IsStandaloneTransportEnabled()) &&
          (bypass_setup_in_progress_check || !IsSetupInProgress());
 }
 
@@ -1317,9 +1287,7 @@
     // TODO(crbug.com/856179): Evaluate whether we can get away without a full
     // restart (i.e. just reconfigure plus whatever cleanup is necessary). See
     // also similar comment in OnSyncRequestedPrefChange().
-    if (IsStandaloneTransportEnabled()) {
-      startup_controller_->TryStart(/*force_immediate=*/false);
-    }
+    startup_controller_->TryStart(/*force_immediate=*/false);
   }
 }
 
@@ -1364,7 +1332,6 @@
   syncer::ModelTypeSet types = GetPreferredDataTypes();
   // In transport-only mode, only a subset of data types is supported.
   if (use_transport_only_mode) {
-    DCHECK(IsStandaloneTransportEnabled());
     syncer::ModelTypeSet allowed_types = {syncer::USER_CONSENTS};
 
     if (base::FeatureList::IsEnabled(
@@ -1560,7 +1527,7 @@
 
     // If the Sync engine was already initialized (probably running in transport
     // mode), just reconfigure.
-    if (IsStandaloneTransportEnabled() && engine_initialized_) {
+    if (engine_initialized_) {
       ReconfigureDatatypeManager(/*bypass_setup_in_progress_check=*/false);
     } else {
       // Otherwise try to start up. Note that there might still be other disable
@@ -1580,9 +1547,7 @@
     // restart (i.e. just reconfigure plus whatever cleanup is necessary).
     // Especially in the CLEAR_DATA case, StopImpl does a lot of cleanup that
     // might still be required.
-    if (IsStandaloneTransportEnabled()) {
-      startup_controller_->TryStart(/*force_immediate=*/false);
-    }
+    startup_controller_->TryStart(/*force_immediate=*/false);
   }
 }
 
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index f33128894..6c01395b 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -373,9 +373,6 @@
 
   bool IsEngineAllowedToStart() const;
 
-  // Callback for StartupController.
-  bool ShouldStartEngine(bool bypass_first_setup_check) const;
-
   enum UnrecoverableErrorReason {
     ERROR_REASON_UNSET,
     ERROR_REASON_SYNCER,
diff --git a/components/browser_sync/profile_sync_service_startup_unittest.cc b/components/browser_sync/profile_sync_service_startup_unittest.cc
index 4d17637e..45a649b7 100644
--- a/components/browser_sync/profile_sync_service_startup_unittest.cc
+++ b/components/browser_sync/profile_sync_service_startup_unittest.cc
@@ -156,110 +156,10 @@
   std::unique_ptr<ProfileSyncService> sync_service_;
 };
 
-class ProfileSyncServiceWithStandaloneTransportStartupTest
-    : public ProfileSyncServiceStartupTest {
- protected:
-  ProfileSyncServiceWithStandaloneTransportStartupTest() {
-    feature_list_.InitAndEnableFeature(switches::kSyncStandaloneTransport);
-  }
-
-  ~ProfileSyncServiceWithStandaloneTransportStartupTest() override {}
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-class ProfileSyncServiceWithoutStandaloneTransportStartupTest
-    : public ProfileSyncServiceStartupTest {
- protected:
-  ProfileSyncServiceWithoutStandaloneTransportStartupTest() {
-    feature_list_.InitAndDisableFeature(switches::kSyncStandaloneTransport);
-  }
-
-  ~ProfileSyncServiceWithoutStandaloneTransportStartupTest() override {}
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // ChromeOS does not support sign-in after startup (in particular,
 // IdentityManager::Observer::OnPrimaryAccountSet never gets called).
 #if !defined(OS_CHROMEOS)
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportStartupTest,
-       StartFirstTime) {
-  // We've never completed startup.
-  ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
-
-  CreateSyncService(ProfileSyncService::MANUAL_START);
-  SetUpFakeSyncEngine();
-  DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
-  EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::STOPPED));
-
-  // Should not actually start, rather just clean things up and wait
-  // to be enabled.
-  sync_service()->Initialize();
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
-            sync_service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            sync_service()->GetTransportState());
-
-  // Preferences should be back to defaults.
-  EXPECT_EQ(base::Time(), sync_prefs()->GetLastSyncedTime());
-  EXPECT_FALSE(sync_prefs()->IsFirstSetupComplete());
-
-  // This tells the ProfileSyncService that setup is now in progress, which
-  // causes it to try starting up the engine. We're not signed in yet though, so
-  // that won't work.
-  auto sync_blocker = sync_service()->GetSetupInProgressHandle();
-  EXPECT_FALSE(sync_service()->IsEngineInitialized());
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
-            sync_service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            sync_service()->GetTransportState());
-
-  // Simulate successful signin. This will cause ProfileSyncService to start,
-  // since all conditions are now fulfilled.
-  SimulateTestUserSignin();
-
-  // Now we're signed in, so the engine can start. There's already a setup in
-  // progress, so we don't go into the WAITING_FOR_START_REQUEST state. Engine
-  // initialization is immediate in this test, so we also bypass the
-  // INITIALIZING state.
-  EXPECT_TRUE(sync_service()->IsEngineInitialized());
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            sync_service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
-            sync_service()->GetTransportState());
-
-  // Simulate the UI telling sync it has finished setting up. Note that this is
-  // a two-step process: Releasing the SetupInProgressHandle, and marking first
-  // setup complete.
-  sync_blocker.reset();
-  // Now setup isn't in progress anymore, but Sync is still waiting to be told
-  // that the initial setup was completed.
-  ASSERT_FALSE(sync_service()->IsSetupInProgress());
-  EXPECT_EQ(syncer::SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
-            sync_service()->GetTransportState());
-
-  // Marking first setup complete will let ProfileSyncService configure the
-  // DataTypeManager.
-  EXPECT_CALL(*data_type_manager, Configure(_, _));
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::CONFIGURED));
-  sync_service()->GetUserSettings()->SetFirstSetupComplete();
-
-  // This should have fully enabled sync.
-  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            sync_service()->GetTransportState());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
-
-  EXPECT_CALL(*data_type_manager, Stop(syncer::BROWSER_SHUTDOWN));
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportStartupTest, StartFirstTime) {
+TEST_F(ProfileSyncServiceStartupTest, StartFirstTime) {
   // We've never completed startup.
   ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
 
@@ -463,26 +363,7 @@
   EXPECT_CALL(*data_type_manager, Stop(syncer::BROWSER_SHUTDOWN));
 }
 
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportStartupTest, StopSync) {
-  sync_prefs()->SetFirstSetupComplete();
-  CreateSyncService(ProfileSyncService::MANUAL_START);
-  SimulateTestUserSignin();
-  SetUpFakeSyncEngine();
-  DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::CONFIGURED));
-  ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
-
-  sync_service()->Initialize();
-
-  EXPECT_CALL(*data_type_manager, Stop(syncer::STOP_SYNC));
-  sync_service()->GetUserSettings()->SetSyncRequested(false);
-
-  EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportStartupTest, StopSync) {
+TEST_F(ProfileSyncServiceStartupTest, StopSync) {
   sync_prefs()->SetFirstSetupComplete();
   CreateSyncService(ProfileSyncService::MANUAL_START);
   SimulateTestUserSignin();
@@ -507,26 +388,7 @@
   EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
 }
 
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportStartupTest, DisableSync) {
-  sync_prefs()->SetFirstSetupComplete();
-  CreateSyncService(ProfileSyncService::MANUAL_START);
-  SimulateTestUserSignin();
-  SetUpFakeSyncEngine();
-  DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::CONFIGURED));
-  ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
-
-  sync_service()->Initialize();
-
-  EXPECT_CALL(*data_type_manager, Stop(syncer::DISABLE_SYNC));
-  sync_service()->StopAndClear();
-
-  EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportStartupTest, DisableSync) {
+TEST_F(ProfileSyncServiceStartupTest, DisableSync) {
   sync_prefs()->SetFirstSetupComplete();
   CreateSyncService(ProfileSyncService::MANUAL_START);
   SimulateTestUserSignin();
@@ -615,58 +477,7 @@
             sync_service()->GetDisableReasons());
 }
 
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportStartupTest, SwitchManaged) {
-  sync_prefs()->SetFirstSetupComplete();
-  CreateSyncService(ProfileSyncService::MANUAL_START);
-  SimulateTestUserSignin();
-  SetUpFakeSyncEngine();
-  DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
-  EXPECT_CALL(*data_type_manager, Configure(_, _));
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::CONFIGURED));
-  ON_CALL(*data_type_manager, IsNigoriEnabled()).WillByDefault(Return(true));
-  sync_service()->Initialize();
-  EXPECT_TRUE(sync_service()->IsEngineInitialized());
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            sync_service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            sync_service()->GetTransportState());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
-
-  // The service should stop when switching to managed mode.
-  Mock::VerifyAndClearExpectations(data_type_manager);
-  EXPECT_CALL(*data_type_manager, state())
-      .WillOnce(Return(DataTypeManager::CONFIGURED));
-  EXPECT_CALL(*data_type_manager, Stop(syncer::DISABLE_SYNC));
-  sync_prefs()->SetManagedForTest(true);
-  ASSERT_EQ(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
-            sync_service()->GetDisableReasons());
-  EXPECT_FALSE(sync_service()->IsEngineInitialized());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            sync_service()->GetTransportState());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
-  // Note that PSS no longer references |data_type_manager| after stopping.
-
-  // When switching back to unmanaged, the state should change but sync should
-  // not start automatically because IsFirstSetupComplete() will be false and
-  // no setup is in progress.
-  // A new DataTypeManager should not be created.
-  Mock::VerifyAndClearExpectations(data_type_manager);
-  EXPECT_CALL(*component_factory(), CreateDataTypeManager(_, _, _, _, _, _))
-      .Times(0);
-  sync_prefs()->SetManagedForTest(false);
-  ASSERT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            sync_service()->GetDisableReasons());
-  EXPECT_FALSE(sync_service()->IsEngineInitialized());
-  EXPECT_EQ(syncer::SyncService::TransportState::WAITING_FOR_START_REQUEST,
-            sync_service()->GetTransportState());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportStartupTest, SwitchManaged) {
+TEST_F(ProfileSyncServiceStartupTest, SwitchManaged) {
   sync_prefs()->SetFirstSetupComplete();
   CreateSyncService(ProfileSyncService::MANUAL_START);
   SimulateTestUserSignin();
@@ -767,94 +578,7 @@
 // ChromeOS does not support sign-in after startup (in particular,
 // IdentityManager::Observer::OnPrimaryAccountSet never gets called).
 #if !defined(OS_CHROMEOS)
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportStartupTest,
-       FullStartupSequenceFirstTime) {
-  // We've never completed startup.
-  ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
-
-  MockSyncEngine* sync_engine = SetUpMockSyncEngine();
-  EXPECT_CALL(*sync_engine, Initialize(_)).Times(0);
-
-  DataTypeManagerMock* data_type_manager = SetUpDataTypeManagerMock();
-  EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0);
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::STOPPED));
-
-  // Note: Deferred startup is only enabled if SESSIONS is among the preferred
-  // data types.
-  CreateSyncService(ProfileSyncService::MANUAL_START,
-                    syncer::ModelTypeSet(syncer::SESSIONS));
-  sync_service()->Initialize();
-
-  // There is no signed-in user, but nothing else prevents Sync from starting.
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN,
-            sync_service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            sync_service()->GetTransportState());
-
-  // Sign in. Now Sync is ready to start, just waiting for a prod.
-  SimulateTestUserSignin();
-  EXPECT_EQ(syncer::SyncService::TransportState::WAITING_FOR_START_REQUEST,
-            sync_service()->GetTransportState());
-
-  // Once we give the service a prod by initiating Sync setup, it'll start and
-  // initialize the engine. Since this is the initial Sync start, this will not
-  // be deferred.
-  EXPECT_CALL(*sync_engine, Initialize(_));
-  auto setup_in_progress_handle = sync_service()->GetSetupInProgressHandle();
-  EXPECT_EQ(syncer::SyncService::TransportState::INITIALIZING,
-            sync_service()->GetTransportState());
-
-  // Once the engine calls back and says it's initialized, we're just waiting
-  // for the user to finish the initial configuration (choosing data types etc.)
-  // before actually syncing data.
-  sync_service()->OnEngineInitialized(
-      syncer::ModelTypeSet(), syncer::WeakHandle<syncer::JsBackend>(),
-      syncer::WeakHandle<syncer::DataTypeDebugInfoListener>(), "test-guid",
-      "test-session-name", "test-birthday", "test-bag-of-chips",
-      /*success=*/true);
-  ASSERT_TRUE(sync_service()->IsEngineInitialized());
-  EXPECT_EQ(syncer::SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
-            sync_service()->GetTransportState());
-  EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
-
-  // Once the user finishes the initial setup, the service can actually start
-  // configuring the data types. Just marking the initial setup as complete
-  // isn't enough though, because setup is still considered in progress (we
-  // haven't released the setup-in-progress handle).
-  sync_service()->GetUserSettings()->SetFirstSetupComplete();
-  EXPECT_EQ(syncer::SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
-            sync_service()->GetTransportState());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
-
-  // Releasing the setup in progress handle lets the service actually configure
-  // the DataTypeManager.
-  EXPECT_CALL(*data_type_manager, Configure(_, _))
-      .WillOnce(InvokeWithoutArgs(sync_service(),
-                                  &ProfileSyncService::OnConfigureStart));
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::CONFIGURING));
-  setup_in_progress_handle.reset();
-  // While DataTypeManager configuration is ongoing, the overall state is still
-  // CONFIGURING.
-  EXPECT_EQ(syncer::SyncService::TransportState::CONFIGURING,
-            sync_service()->GetTransportState());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
-
-  // Finally, once the DataTypeManager says it's done with configuration, Sync
-  // is actually fully up and running.
-  DataTypeManager::ConfigureResult configure_result(
-      DataTypeManager::OK, syncer::ModelTypeSet(syncer::SESSIONS));
-  ON_CALL(*data_type_manager, state())
-      .WillByDefault(Return(DataTypeManager::CONFIGURED));
-  sync_service()->OnConfigureDone(configure_result);
-  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            sync_service()->GetTransportState());
-  EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportStartupTest,
-       FullStartupSequenceFirstTime) {
+TEST_F(ProfileSyncServiceStartupTest, FullStartupSequenceFirstTime) {
   // We've never completed startup.
   ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
 
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index d036de25..33fab58 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -298,32 +298,6 @@
   std::unique_ptr<ProfileSyncService> service_;
 };
 
-class ProfileSyncServiceWithStandaloneTransportTest
-    : public ProfileSyncServiceTest {
- protected:
-  ProfileSyncServiceWithStandaloneTransportTest() {
-    feature_list_.InitAndEnableFeature(switches::kSyncStandaloneTransport);
-  }
-
-  ~ProfileSyncServiceWithStandaloneTransportTest() override {}
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-class ProfileSyncServiceWithoutStandaloneTransportTest
-    : public ProfileSyncServiceTest {
- protected:
-  ProfileSyncServiceWithoutStandaloneTransportTest() {
-    feature_list_.InitAndDisableFeature(switches::kSyncStandaloneTransport);
-  }
-
-  ~ProfileSyncServiceWithoutStandaloneTransportTest() override {}
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
 // Verify that the server URLs are sane.
 TEST_F(ProfileSyncServiceTest, InitialState) {
   CreateService(ProfileSyncService::AUTO_START);
@@ -363,41 +337,8 @@
 }
 
 // Verify that an initialization where first setup is not complete does not
-// start up the backend.
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportTest, NeedsConfirmation) {
-  SignIn();
-  CreateService(ProfileSyncService::MANUAL_START);
-
-  syncer::SyncPrefs sync_prefs(prefs());
-  base::Time now = base::Time::Now();
-  sync_prefs.SetLastSyncedTime(now);
-  sync_prefs.SetDataTypesConfiguration(/*keep_everything_synced=*/true,
-                                       syncer::UserTypes(),
-                                       syncer::UserSelectableTypes());
-  service()->Initialize();
-
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            service()->GetDisableReasons());
-  // Note: At this point the engine *can* start, but nothing has kicked it off
-  // (usually that happens via getting and then releasing a
-  // SyncSetupInProgressHandle), so the state is still WAITING_FOR_START_REQUEST
-  // and not PENDING_DESIRED_CONFIGURATION.
-  EXPECT_EQ(syncer::SyncService::TransportState::WAITING_FOR_START_REQUEST,
-            service()->GetTransportState());
-
-  // Once we kick off initialization by getting and releasing a setup handle,
-  // the state goes to PENDING_DESIRED_CONFIGURATION.
-  service()->GetSetupInProgressHandle();
-  EXPECT_EQ(syncer::SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
-            service()->GetTransportState());
-
-  // The last sync time shouldn't be cleared.
-  // TODO(zea): figure out a way to check that the directory itself wasn't
-  // cleared.
-  EXPECT_EQ(now, sync_prefs.GetLastSyncedTime());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportTest, NeedsConfirmation) {
+// start up Sync-the-feature.
+TEST_F(ProfileSyncServiceTest, NeedsConfirmation) {
   SignIn();
   CreateService(ProfileSyncService::MANUAL_START);
 
@@ -521,39 +462,7 @@
 }
 
 // Test SetSyncRequested(false) before we've initialized the backend.
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportTest, EarlyRequestStop) {
-  CreateService(ProfileSyncService::AUTO_START);
-  // Set up a fake sync engine that will not immediately finish initialization.
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
-      .WillOnce(ReturnNewFakeSyncEngineNoReturn());
-  SignIn();
-  InitializeForNthSync();
-
-  ASSERT_EQ(syncer::SyncService::TransportState::INITIALIZING,
-            service()->GetTransportState());
-
-  // Request stop. Sync should get disabled.
-  service()->GetUserSettings()->SetSyncRequested(false);
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
-            service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-  EXPECT_FALSE(service()->IsSyncFeatureEnabled());
-
-  // Request start again, this time with an engine that does get initialized.
-  // Sync should become active.
-  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
-      .WillOnce(ReturnNewFakeSyncEngine());
-  service()->GetUserSettings()->SetSyncRequested(true);
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_TRUE(service()->IsSyncFeatureActive());
-  EXPECT_TRUE(service()->IsSyncFeatureEnabled());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportTest, EarlyRequestStop) {
+TEST_F(ProfileSyncServiceTest, EarlyRequestStop) {
   CreateService(ProfileSyncService::AUTO_START);
   // Set up a fake sync engine that will not immediately finish initialization.
   EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
@@ -587,43 +496,7 @@
 }
 
 // Test SetSyncRequested(false) after we've initialized the backend.
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportTest,
-       DisableAndEnableSyncTemporarily) {
-  CreateService(ProfileSyncService::AUTO_START);
-  SignIn();
-  InitializeForNthSync();
-
-  ASSERT_FALSE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
-  ASSERT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            service()->GetDisableReasons());
-  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  ASSERT_TRUE(service()->IsSyncFeatureActive());
-  ASSERT_TRUE(service()->IsSyncFeatureEnabled());
-
-  testing::Mock::VerifyAndClearExpectations(component_factory());
-
-  service()->GetUserSettings()->SetSyncRequested(false);
-  EXPECT_TRUE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
-            service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-  EXPECT_FALSE(service()->IsSyncFeatureActive());
-  EXPECT_FALSE(service()->IsSyncFeatureEnabled());
-
-  service()->GetUserSettings()->SetSyncRequested(true);
-  EXPECT_FALSE(prefs()->GetBoolean(syncer::prefs::kSyncSuppressStart));
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NONE,
-            service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  EXPECT_TRUE(service()->IsSyncFeatureActive());
-  EXPECT_TRUE(service()->IsSyncFeatureEnabled());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportTest,
-       DisableAndEnableSyncTemporarily) {
+TEST_F(ProfileSyncServiceTest, DisableAndEnableSyncTemporarily) {
   CreateService(ProfileSyncService::AUTO_START);
   SignIn();
   InitializeForNthSync();
@@ -771,7 +644,10 @@
 
 // Checks that CREDENTIALS_REJECTED_BY_CLIENT resets the access token and stops
 // Sync. Regression test for https://crbug.com/824791.
-TEST_F(ProfileSyncServiceTest, CredentialsRejectedByClient) {
+TEST_F(ProfileSyncServiceTest, CredentialsRejectedByClient_StopSync) {
+  base::test::ScopedFeatureList feature;
+  feature.InitAndEnableFeature(switches::kStopSyncInPausedState);
+
   syncer::SyncCredentials init_credentials;
 
   CreateService(ProfileSyncService::AUTO_START);
@@ -830,6 +706,72 @@
   service()->RemoveObserver(&observer);
 }
 
+TEST_F(ProfileSyncServiceTest, CredentialsRejectedByClient_DoNotStopSync) {
+  base::test::ScopedFeatureList feature;
+  feature.InitAndDisableFeature(switches::kStopSyncInPausedState);
+
+  syncer::SyncCredentials init_credentials;
+
+  bool invalidate_credentials_called = false;
+  base::RepeatingClosure invalidate_credentials_callback =
+      base::BindRepeating([](bool* called) { *called = true; },
+                          base::Unretained(&invalidate_credentials_called));
+
+  CreateService(ProfileSyncService::AUTO_START);
+  SignIn();
+  EXPECT_CALL(*component_factory(), CreateSyncEngine(_, _, _))
+      .WillOnce(
+          Return(ByMove(std::make_unique<FakeSyncEngineCollectCredentials>(
+              &init_credentials, invalidate_credentials_callback))));
+  InitializeForNthSync();
+  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
+            service()->GetTransportState());
+
+  TestSyncServiceObserver observer;
+  service()->AddObserver(&observer);
+
+  const std::string primary_account_id =
+      identity_manager()->GetPrimaryAccountId();
+
+  // Make sure the expected credentials (correct account_id, empty access token)
+  // were passed to the SyncEngine.
+  ASSERT_EQ(primary_account_id, init_credentials.account_id);
+  ASSERT_TRUE(init_credentials.sync_token.empty());
+
+  // At this point, the real SyncEngine would try to connect to the server, fail
+  // (because it has no access token), and eventually call
+  // OnConnectionStatusChange(CONNECTION_AUTH_ERROR). Since our fake SyncEngine
+  // doesn't do any of this, call that explicitly here.
+  service()->OnConnectionStatusChange(syncer::CONNECTION_AUTH_ERROR);
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(service()->GetAccessTokenForTest().empty());
+  ASSERT_EQ(GoogleServiceAuthError::AuthErrorNone(), service()->GetAuthError());
+  ASSERT_EQ(GoogleServiceAuthError::AuthErrorNone(), observer.auth_error());
+
+  // Simulate the credentials getting locally rejected by the client by setting
+  // the refresh token to a special invalid value.
+  identity_test_env()->SetInvalidRefreshTokenForPrimaryAccount();
+  const GoogleServiceAuthError rejected_by_client =
+      GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
+          GoogleServiceAuthError::InvalidGaiaCredentialsReason::
+              CREDENTIALS_REJECTED_BY_CLIENT);
+  ASSERT_EQ(rejected_by_client,
+            identity_test_env()
+                ->identity_manager()
+                ->GetErrorStateOfRefreshTokenForAccount(primary_account_id));
+  EXPECT_TRUE(service()->GetAccessTokenForTest().empty());
+  EXPECT_TRUE(invalidate_credentials_called);
+
+  // The observer should have been notified of the auth error state.
+  EXPECT_EQ(rejected_by_client, observer.auth_error());
+  // The Sync engine should still be running.
+  EXPECT_EQ(syncer::SyncService::TransportState::ACTIVE,
+            service()->GetTransportState());
+
+  service()->RemoveObserver(&observer);
+}
+
 // CrOS does not support signout.
 #if !defined(OS_CHROMEOS)
 TEST_F(ProfileSyncServiceTest, SignOutRevokeAccessToken) {
@@ -876,28 +818,7 @@
 #endif
 
 // Verify that LastSyncedTime and local DeviceInfo is cleared on sign out.
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportTest, ClearDataOnSignOut) {
-  SignIn();
-  CreateService(ProfileSyncService::AUTO_START);
-  InitializeForNthSync();
-  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  ASSERT_LT(base::Time::Now() - service()->GetLastSyncedTime(),
-            base::TimeDelta::FromMinutes(1));
-  ASSERT_TRUE(local_device_info_provider()->GetLocalDeviceInfo());
-
-  // Sign out.
-  service()->StopAndClear();
-
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-  EXPECT_FALSE(service()->IsSyncFeatureEnabled());
-
-  EXPECT_TRUE(service()->GetLastSyncedTime().is_null());
-  EXPECT_FALSE(local_device_info_provider()->GetLocalDeviceInfo());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportTest, ClearDataOnSignOut) {
+TEST_F(ProfileSyncServiceTest, ClearDataOnSignOut) {
   SignIn();
   CreateService(ProfileSyncService::AUTO_START);
   InitializeForNthSync();
@@ -921,7 +842,7 @@
   EXPECT_TRUE(local_device_info_provider()->GetLocalDeviceInfo());
 }
 
-TEST_F(ProfileSyncServiceWithStandaloneTransportTest, CancelSyncAfterSignOut) {
+TEST_F(ProfileSyncServiceTest, CancelSyncAfterSignOut) {
   SignIn();
   CreateService(ProfileSyncService::AUTO_START);
   InitializeForNthSync();
@@ -1185,44 +1106,7 @@
 
 // Test that when ProfileSyncService receives actionable error
 // DISABLE_SYNC_ON_CLIENT it disables sync and signs out.
-TEST_F(ProfileSyncServiceWithoutStandaloneTransportTest, DisableSyncOnClient) {
-  SignIn();
-  CreateService(ProfileSyncService::AUTO_START);
-  InitializeForNthSync();
-
-  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
-            service()->GetTransportState());
-  ASSERT_LT(base::Time::Now() - service()->GetLastSyncedTime(),
-            base::TimeDelta::FromMinutes(1));
-  ASSERT_TRUE(local_device_info_provider()->GetLocalDeviceInfo());
-
-  syncer::SyncProtocolError client_cmd;
-  client_cmd.action = syncer::DISABLE_SYNC_ON_CLIENT;
-  service()->OnActionableError(client_cmd);
-
-#if defined(OS_CHROMEOS)
-  // ChromeOS does not support signout.
-  EXPECT_TRUE(identity_manager()->HasPrimaryAccount());
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_USER_CHOICE,
-            service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-#else
-  EXPECT_FALSE(identity_manager()->HasPrimaryAccount());
-  EXPECT_EQ(syncer::SyncService::DISABLE_REASON_NOT_SIGNED_IN |
-                syncer::SyncService::DISABLE_REASON_USER_CHOICE,
-            service()->GetDisableReasons());
-  EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
-            service()->GetTransportState());
-  EXPECT_TRUE(service()->GetLastSyncedTime().is_null());
-  EXPECT_FALSE(local_device_info_provider()->GetLocalDeviceInfo());
-#endif
-
-  EXPECT_FALSE(service()->IsSyncFeatureEnabled());
-  EXPECT_FALSE(service()->IsSyncFeatureActive());
-}
-
-TEST_F(ProfileSyncServiceWithStandaloneTransportTest, DisableSyncOnClient) {
+TEST_F(ProfileSyncServiceTest, DisableSyncOnClient) {
   SignIn();
   CreateService(ProfileSyncService::AUTO_START);
   InitializeForNthSync();
diff --git a/components/browser_sync/sync_auth_manager.cc b/components/browser_sync/sync_auth_manager.cc
index ee799f3..7bfa58f 100644
--- a/components/browser_sync/sync_auth_manager.cc
+++ b/components/browser_sync/sync_auth_manager.cc
@@ -357,8 +357,7 @@
   DCHECK(registered_for_auth_notifications_);
   return syncer::DetermineAccountToUse(
       identity_manager_,
-      base::FeatureList::IsEnabled(switches::kSyncStandaloneTransport) &&
-          base::FeatureList::IsEnabled(switches::kSyncSupportSecondaryAccount));
+      base::FeatureList::IsEnabled(switches::kSyncSupportSecondaryAccount));
 }
 
 bool SyncAuthManager::UpdateSyncAccountIfNecessary() {
diff --git a/components/browser_sync/sync_auth_manager_unittest.cc b/components/browser_sync/sync_auth_manager_unittest.cc
index 24f2376..e0509f7 100644
--- a/components/browser_sync/sync_auth_manager_unittest.cc
+++ b/components/browser_sync/sync_auth_manager_unittest.cc
@@ -600,10 +600,7 @@
 
 TEST_F(SyncAuthManagerTest, UsesCookieJarIfFeatureEnabled) {
   base::test::ScopedFeatureList features;
-  features.InitWithFeatures(
-      /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                            switches::kSyncSupportSecondaryAccount},
-      /*disabled_features=*/{});
+  features.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
 
   auto auth_manager = CreateAuthManager();
   auth_manager->RegisterForAuthNotifications();
@@ -624,10 +621,7 @@
 
 TEST_F(SyncAuthManagerTest, DropsAccountWhenCookieGoesAway) {
   base::test::ScopedFeatureList features;
-  features.InitWithFeatures(
-      /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                            switches::kSyncSupportSecondaryAccount},
-      /*disabled_features=*/{});
+  features.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
 
   auto auth_manager = CreateAuthManager();
   auth_manager->RegisterForAuthNotifications();
@@ -653,10 +647,7 @@
 
 TEST_F(SyncAuthManagerTest, DropsAccountWhenRefreshTokenGoesAway) {
   base::test::ScopedFeatureList features;
-  features.InitWithFeatures(
-      /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                            switches::kSyncSupportSecondaryAccount},
-      /*disabled_features=*/{});
+  features.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
 
   auto auth_manager = CreateAuthManager();
   auth_manager->RegisterForAuthNotifications();
@@ -682,10 +673,7 @@
 
 TEST_F(SyncAuthManagerTest, PrefersPrimaryAccountOverCookie) {
   base::test::ScopedFeatureList features;
-  features.InitWithFeatures(
-      /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                            switches::kSyncSupportSecondaryAccount},
-      /*disabled_features=*/{});
+  features.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
 
   auto auth_manager = CreateAuthManager();
   auth_manager->RegisterForAuthNotifications();
@@ -711,10 +699,7 @@
 
 TEST_F(SyncAuthManagerTest, OnlyUsesFirstCookieAccount) {
   base::test::ScopedFeatureList features;
-  features.InitWithFeatures(
-      /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                            switches::kSyncSupportSecondaryAccount},
-      /*disabled_features=*/{});
+  features.InitAndEnableFeature(switches::kSyncSupportSecondaryAccount);
 
   auto auth_manager = CreateAuthManager();
   auth_manager->RegisterForAuthNotifications();
diff --git a/components/certificate_transparency/log_dns_client.cc b/components/certificate_transparency/log_dns_client.cc
index d02e206..3d47b4a 100644
--- a/components/certificate_transparency/log_dns_client.cc
+++ b/components/certificate_transparency/log_dns_client.cc
@@ -168,12 +168,15 @@
   ~AuditProofQueryImpl() override;
 
   // Begins the process of getting an audit proof for the CT log entry with a
-  // leaf hash of |leaf_hash|. The proof will be for a tree of size |tree_size|.
-  // If it cannot be obtained synchronously, net::ERR_IO_PENDING will be
-  // returned and |callback| will be invoked when the operation has completed
-  // asynchronously. If the operation is cancelled (by deleting the
-  // AuditProofQueryImpl), |cancellation_callback| will be invoked.
+  // leaf hash of |leaf_hash|. If |lookup_securely| is true, only secure DNS
+  // lookups will be performed, otherwise only insecure DNS lookups will be
+  // performed. The proof will be for a tree of size |tree_size|. If the proof
+  // cannot be obtained synchronously, net::ERR_IO_PENDING will be returned and
+  // |callback| will be invoked when the operation has completed asynchronously.
+  // If the operation is cancelled (by deleting the AuditProofQueryImpl),
+  // |cancellation_callback| will be invoked.
   net::Error Start(std::string leaf_hash,
+                   bool lookup_securely,
                    uint64_t tree_size,
                    net::CompletionOnceCallback callback,
                    base::OnceClosure cancellation_callback);
@@ -242,6 +245,8 @@
   std::string domain_for_log_;
   // The Merkle leaf hash of the CT log entry an audit proof is required for.
   std::string leaf_hash_;
+  // Whether the DNS request should be sent securely or insecurely.
+  bool lookup_securely_;
   // The audit proof to populate.
   net::ct::MerkleAuditProof proof_;
   // The callback to invoke when the query is complete.
@@ -289,6 +294,7 @@
 // |leaf_hash| is not a const-ref to allow callers to std::move that string into
 // the method, avoiding the need to make a copy.
 net::Error AuditProofQueryImpl::Start(std::string leaf_hash,
+                                      bool lookup_securely,
                                       uint64_t tree_size,
                                       net::CompletionOnceCallback callback,
                                       base::OnceClosure cancellation_callback) {
@@ -297,6 +303,7 @@
   start_time_ = base::TimeTicks::Now();
   proof_.tree_size = tree_size;
   leaf_hash_ = std::move(leaf_hash);
+  lookup_securely_ = lookup_securely;
   callback_ = std::move(callback);
   cancellation_callback_ = std::move(cancellation_callback);
   // The first step in the query is to request the leaf index corresponding to
@@ -480,7 +487,8 @@
       qname, net::dns_protocol::kTypeTXT,
       base::BindOnce(&AuditProofQueryImpl::OnDnsTransactionComplete,
                      weak_ptr_factory_.GetWeakPtr()),
-      net_log_, net::SecureDnsMode::AUTOMATIC);
+      net_log_,
+      lookup_securely_ ? net::SecureDnsMode::SECURE : net::SecureDnsMode::OFF);
   DCHECK(url_request_context_);
   current_dns_transaction_->SetRequestContext(url_request_context_);
 
@@ -524,6 +532,7 @@
 net::Error LogDnsClient::QueryAuditProof(
     base::StringPiece domain_for_log,
     std::string leaf_hash,
+    bool lookup_securely,
     uint64_t tree_size,
     std::unique_ptr<AuditProofQuery>* out_query,
     const net::CompletionCallback& callback) {
@@ -543,7 +552,7 @@
 
   ++in_flight_queries_;
 
-  return query->Start(std::move(leaf_hash), tree_size,
+  return query->Start(std::move(leaf_hash), lookup_securely, tree_size,
                       base::BindOnce(&LogDnsClient::QueryAuditProofComplete,
                                      base::Unretained(this), callback),
                       base::BindOnce(&LogDnsClient::QueryAuditProofCancelled,
diff --git a/components/certificate_transparency/log_dns_client.h b/components/certificate_transparency/log_dns_client.h
index 31f2bab..0376f61 100644
--- a/components/certificate_transparency/log_dns_client.h
+++ b/components/certificate_transparency/log_dns_client.h
@@ -78,6 +78,10 @@
   // The |leaf_hash| is the SHA-256 Merkle leaf hash (see RFC6962, section 2.1).
   // The size of the CT log tree, for which the proof is requested, must be
   // provided in |tree_size|.
+  // The field |lookup_securely| specifies whether DNS lookups should be
+  // performed securely or insecurely. This value should be set according to
+  // whether the hostname lookup was resolved securely or not in order to
+  // help achieve resolver consistency between the hostname and proof lookups.
   // A handle to the query will be placed in |out_query|. The audit proof can be
   // obtained from that once the query completes. Deleting this handle before
   // the query completes will cancel it. It must not outlive the LogDnsClient.
@@ -96,6 +100,7 @@
   //   not a SHA-256 hash.
   net::Error QueryAuditProof(base::StringPiece domain_for_log,
                              std::string leaf_hash,
+                             bool lookup_securely,
                              uint64_t tree_size,
                              std::unique_ptr<AuditProofQuery>* out_query,
                              const net::CompletionCallback& callback);
diff --git a/components/certificate_transparency/log_dns_client_unittest.cc b/components/certificate_transparency/log_dns_client_unittest.cc
index 90776427..93da272 100644
--- a/components/certificate_transparency/log_dns_client_unittest.cc
+++ b/components/certificate_transparency/log_dns_client_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/cert/signed_certificate_timestamp.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
+#include "net/dns/dns_test_util.h"
 #include "net/dns/public/dns_protocol.h"
 #include "net/log/net_log.h"
 #include "net/test/gtest_util.h"
@@ -132,6 +133,14 @@
         net::NetLogWithSource(), max_concurrent_queries);
   }
 
+  std::unique_ptr<LogDnsClient> CreateRuleBasedLogDnsClient(
+      net::MockDnsClientRuleList rules) {
+    return std::make_unique<LogDnsClient>(
+        std::make_unique<net::MockDnsClient>(net::DnsConfig(),
+                                             std::move(rules)),
+        new net::TestURLRequestContext(), net::NetLogWithSource(), 0);
+  }
+
   // Convenience function for calling QueryAuditProof synchronously.
   template <typename... Types>
   net::Error QueryAuditProof(Types&&... args) {
@@ -163,8 +172,10 @@
       kLeafIndexQnames[0], net::dns_protocol::kRcodeNXDOMAIN));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], kTreeSizes[0], &query),
-              IsError(net::ERR_NAME_NOT_RESOLVED));
+  ASSERT_THAT(
+      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
+                      kTreeSizes[0], &query),
+      IsError(net::ERR_NAME_NOT_RESOLVED));
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
                                  -net::ERR_NAME_NOT_RESOLVED, 1);
   histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
@@ -179,8 +190,10 @@
       kLeafIndexQnames[0], net::dns_protocol::kRcodeSERVFAIL));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], kTreeSizes[0], &query),
-              IsError(net::ERR_DNS_SERVER_FAILED));
+  ASSERT_THAT(
+      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
+                      kTreeSizes[0], &query),
+      IsError(net::ERR_DNS_SERVER_FAILED));
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
                                  -net::ERR_DNS_SERVER_FAILED, 1);
   histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
@@ -195,8 +208,10 @@
       kLeafIndexQnames[0], net::dns_protocol::kRcodeREFUSED));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], kTreeSizes[0], &query),
-              IsError(net::ERR_DNS_SERVER_FAILED));
+  ASSERT_THAT(
+      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
+                      kTreeSizes[0], &query),
+      IsError(net::ERR_DNS_SERVER_FAILED));
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
                                  -net::ERR_DNS_SERVER_FAILED, 1);
   histograms_.ExpectUniqueSample(kLeafIndexRcodeHistogram,
@@ -226,7 +241,8 @@
 
     std::unique_ptr<LogDnsClient::AuditProofQuery> query;
     ASSERT_THAT(
-        QueryAuditProof("ct.test", kLeafHashes[0], kTreeSizes[0], &query),
+        QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
+                        kTreeSizes[0], &query),
         IsError(net::ERR_DNS_MALFORMED_RESPONSE));
     histograms.ExpectUniqueSample(kLeafIndexErrorHistogram,
                                   -net::ERR_DNS_MALFORMED_RESPONSE, 1);
@@ -239,19 +255,22 @@
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsInvalidArgIfLogDomainIsEmpty) {
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("", kLeafHashes[0], kTreeSizes[0], &query),
+  ASSERT_THAT(QueryAuditProof("", kLeafHashes[0], false /* lookup_securely */,
+                              kTreeSizes[0], &query),
               IsError(net::ERR_INVALID_ARGUMENT));
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsInvalidArgIfLeafHashIsInvalid) {
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", "foo", kTreeSizes[0], &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", "foo", false /* lookup_securely */,
+                              kTreeSizes[0], &query),
               IsError(net::ERR_INVALID_ARGUMENT));
 }
 
 TEST_P(LogDnsClientTest, QueryAuditProofReportsInvalidArgIfLeafHashIsEmpty) {
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", "", kTreeSizes[0], &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", "", false /* lookup_securely */,
+                              kTreeSizes[0], &query),
               IsError(net::ERR_INVALID_ARGUMENT));
 }
 
@@ -261,8 +280,10 @@
       kLeafIndexQnames[0], net::ERR_CONNECTION_REFUSED));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], kTreeSizes[0], &query),
-              IsError(net::ERR_CONNECTION_REFUSED));
+  ASSERT_THAT(
+      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
+                      kTreeSizes[0], &query),
+      IsError(net::ERR_CONNECTION_REFUSED));
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
                                  -net::ERR_CONNECTION_REFUSED, 1);
   histograms_.ExpectTotalCount(kLeafIndexRcodeHistogram, 0);
@@ -275,8 +296,10 @@
   ASSERT_TRUE(mock_dns_.ExpectRequestAndTimeout(kLeafIndexQnames[0]));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], kTreeSizes[0], &query),
-              IsError(net::ERR_DNS_TIMED_OUT));
+  ASSERT_THAT(
+      QueryAuditProof("ct.test", kLeafHashes[0], false /* lookup_securely */,
+                      kTreeSizes[0], &query),
+      IsError(net::ERR_DNS_TIMED_OUT));
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram,
                                  -net::ERR_DNS_TIMED_OUT, 1);
   histograms_.ExpectTotalCount(kLeafIndexRcodeHistogram, 0);
@@ -304,7 +327,8 @@
   }
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsOk());
   const net::ct::MerkleAuditProof& proof = query->GetProof();
   EXPECT_THAT(proof.leaf_index, Eq(123456u));
@@ -347,7 +371,8 @@
       audit_proof.end()));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsOk());
   const net::ct::MerkleAuditProof& proof = query->GetProof();
   EXPECT_THAT(proof.leaf_index, Eq(123456u));
@@ -370,7 +395,8 @@
       "0.123456.999999.tree.ct.test.", net::dns_protocol::kRcodeNXDOMAIN));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_NAME_NOT_RESOLVED));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -390,7 +416,8 @@
       "0.123456.999999.tree.ct.test.", net::dns_protocol::kRcodeSERVFAIL));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_SERVER_FAILED));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -410,7 +437,8 @@
       "0.123456.999999.tree.ct.test.", net::dns_protocol::kRcodeREFUSED));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_SERVER_FAILED));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -433,7 +461,8 @@
       "0.123456.999999.tree.ct.test.", std::vector<base::StringPiece>()));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_MALFORMED_RESPONSE));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -466,7 +495,8 @@
       {first_chunk_of_proof, second_chunk_of_proof}));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_MALFORMED_RESPONSE));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -489,7 +519,8 @@
       "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end()));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_MALFORMED_RESPONSE));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -511,7 +542,8 @@
       "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end()));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_MALFORMED_RESPONSE));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -532,7 +564,8 @@
       "0.123456.999999.tree.ct.test.", audit_proof.begin(), audit_proof.end()));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_MALFORMED_RESPONSE));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -550,7 +583,8 @@
       mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 123456, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 123456, &query),
               IsError(net::ERR_INVALID_ARGUMENT));
 }
 
@@ -560,7 +594,8 @@
       mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 999999));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 123456, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 123456, &query),
               IsError(net::ERR_INVALID_ARGUMENT));
 }
 
@@ -572,7 +607,8 @@
       "0.123456.999999.tree.ct.test.", net::ERR_CONNECTION_REFUSED));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_CONNECTION_REFUSED));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -591,7 +627,8 @@
       mock_dns_.ExpectRequestAndTimeout("0.123456.999999.tree.ct.test."));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
-  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0], 999999, &query),
+  ASSERT_THAT(QueryAuditProof("ct.test", kLeafHashes[0],
+                              false /* lookup_securely */, 999999, &query),
               IsError(net::ERR_DNS_TIMED_OUT));
 
   histograms_.ExpectUniqueSample(kLeafIndexErrorHistogram, -net::OK, 1);
@@ -665,7 +702,8 @@
   // Start query.
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
   net::TestCompletionCallback callback;
-  ASSERT_THAT(log_client.QueryAuditProof("ct.test", kLeafHashes[0], 999999,
+  ASSERT_THAT(log_client.QueryAuditProof("ct.test", kLeafHashes[0],
+                                         false /* lookup_securely */, 999999,
                                          &query, callback.callback()),
               IsError(net::ERR_IO_PENDING));
 
@@ -753,10 +791,10 @@
 
   // Start the queries.
   for (size_t i = 0; i < kNumOfParallelQueries; ++i) {
-    ASSERT_THAT(
-        log_client->QueryAuditProof("ct.test", kLeafHashes[i], kTreeSizes[i],
-                                    &queries[i], callbacks[i].callback()),
-        IsError(net::ERR_IO_PENDING))
+    ASSERT_THAT(log_client->QueryAuditProof(
+                    "ct.test", kLeafHashes[i], false /* lookup_securely */,
+                    kTreeSizes[i], &queries[i], callbacks[i].callback()),
+                IsError(net::ERR_IO_PENDING))
         << "query #" << i;
   }
 
@@ -790,19 +828,17 @@
   ASSERT_TRUE(
       mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
 
-  // It should require 3 requests to collect the entire audit proof, as there is
-  // only space for 7 nodes per TXT record. One node is 32 bytes long and the
-  // TXT RDATA can have a maximum length of 255 bytes (255 / 32).
-  // Rate limiting should not interfere with these requests.
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", audit_proof.begin(),
-      audit_proof.begin() + 7));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "7.123456.999999.tree.ct.test.", audit_proof.begin() + 7,
-      audit_proof.begin() + 14));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "14.123456.999999.tree.ct.test.", audit_proof.begin() + 14,
-      audit_proof.end()));
+  // It takes a number of DNS requests to retrieve the entire |audit_proof|
+  // (see |kMaxProofNodesPerDnsResponse|).
+  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
+       nodes_begin += kMaxProofNodesPerDnsResponse) {
+    const size_t nodes_end = std::min(
+        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
+
+    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
+        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
+        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
+  }
 
   const size_t kMaxConcurrentQueries = 1;
   std::unique_ptr<LogDnsClient> log_client =
@@ -811,13 +847,15 @@
   // Try to start the queries.
   std::unique_ptr<LogDnsClient::AuditProofQuery> query1;
   net::TestCompletionCallback callback1;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0], 999999,
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          false /* lookup_securely */, 999999,
                                           &query1, callback1.callback()),
               IsError(net::ERR_IO_PENDING));
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query2;
   net::TestCompletionCallback callback2;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[1], 999999,
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[1],
+                                          false /* lookup_securely */, 999999,
                                           &query2, callback2.callback()),
               IsError(net::ERR_TEMPORARILY_THROTTLED));
 
@@ -831,19 +869,20 @@
   // Try a third query, which should succeed now that the first is finished.
   ASSERT_TRUE(
       mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[2], 666));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.666.999999.tree.ct.test.", audit_proof.begin(),
-      audit_proof.begin() + 7));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "7.666.999999.tree.ct.test.", audit_proof.begin() + 7,
-      audit_proof.begin() + 14));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "14.666.999999.tree.ct.test.", audit_proof.begin() + 14,
-      audit_proof.end()));
+  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
+       nodes_begin += kMaxProofNodesPerDnsResponse) {
+    const size_t nodes_end = std::min(
+        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
+
+    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
+        base::StringPrintf("%zu.666.999999.tree.ct.test.", nodes_begin),
+        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
+  }
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query3;
   net::TestCompletionCallback callback3;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[2], 999999,
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[2],
+                                          false /* lookup_securely */, 999999,
                                           &query3, callback3.callback()),
               IsError(net::ERR_IO_PENDING));
 
@@ -867,15 +906,15 @@
 
   ASSERT_TRUE(
       mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[0], 123456));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.123456.999999.tree.ct.test.", audit_proof.begin(),
-      audit_proof.begin() + 7));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "7.123456.999999.tree.ct.test.", audit_proof.begin() + 7,
-      audit_proof.begin() + 14));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "14.123456.999999.tree.ct.test.", audit_proof.begin() + 14,
-      audit_proof.end()));
+  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
+       nodes_begin += kMaxProofNodesPerDnsResponse) {
+    const size_t nodes_end = std::min(
+        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
+
+    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
+        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
+        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
+  }
 
   const size_t kMaxConcurrentQueries = 1;
   std::unique_ptr<LogDnsClient> log_client =
@@ -884,7 +923,8 @@
   // Start a query.
   std::unique_ptr<LogDnsClient::AuditProofQuery> query1;
   net::TestCompletionCallback query_callback1;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0], 999999,
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          false /* lookup_securely */, 999999,
                                           &query1, query_callback1.callback()),
               IsError(net::ERR_IO_PENDING));
 
@@ -897,19 +937,20 @@
   // Start another query to check |not_throttled_callback| doesn't fire again.
   ASSERT_TRUE(
       mock_dns_.ExpectLeafIndexRequestAndResponse(kLeafIndexQnames[1], 666));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "0.666.999999.tree.ct.test.", audit_proof.begin(),
-      audit_proof.begin() + 7));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "7.666.999999.tree.ct.test.", audit_proof.begin() + 7,
-      audit_proof.begin() + 14));
-  ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
-      "14.666.999999.tree.ct.test.", audit_proof.begin() + 14,
-      audit_proof.end()));
+  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
+       nodes_begin += kMaxProofNodesPerDnsResponse) {
+    const size_t nodes_end = std::min(
+        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
+
+    ASSERT_TRUE(mock_dns_.ExpectAuditProofRequestAndResponse(
+        base::StringPrintf("%zu.666.999999.tree.ct.test.", nodes_begin),
+        audit_proof.begin() + nodes_begin, audit_proof.begin() + nodes_end));
+  }
 
   std::unique_ptr<LogDnsClient::AuditProofQuery> query2;
   net::TestCompletionCallback query_callback2;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[1], 999999,
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[1],
+                                          false /* lookup_securely */, 999999,
                                           &query2, query_callback2.callback()),
               IsError(net::ERR_IO_PENDING));
 
@@ -933,7 +974,8 @@
   // Start query.
   std::unique_ptr<LogDnsClient::AuditProofQuery> query;
   net::TestCompletionCallback callback;
-  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0], 999999,
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          false /* lookup_securely */, 999999,
                                           &query, callback.callback()),
               IsError(net::ERR_IO_PENDING));
 
@@ -950,6 +992,103 @@
   histograms_.ExpectTotalCount(kAuditProofRcodeHistogram, 0);
 }
 
+TEST_P(LogDnsClientTest, SecureDnsMode_Secure) {
+  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
+
+  net::MockDnsClientRuleList rules;
+  // Make leaf index queries for kLeafIndexQnames[0] successful only when
+  // lookup_securely is true.
+  rules.emplace_back(
+      kLeafIndexQnames[0], net::dns_protocol::kTypeTXT,
+      net::SecureDnsMode::SECURE,
+      net::MockDnsClientRule::CreateSecureResult(net::BuildTestDnsResponse(
+          kLeafIndexQnames[0],
+          std::vector<std::vector<std::string>>({{"123456"}}))),
+      false /* delay */);
+
+  // Add successful audit proof queries for lookup_securely true.
+  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
+       nodes_begin += kMaxProofNodesPerDnsResponse) {
+    const size_t nodes_end = std::min(
+        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
+    rules.emplace_back(
+        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
+        net::dns_protocol::kTypeTXT, net::SecureDnsMode::SECURE,
+        net::MockDnsClientRule::CreateSecureResult(net::BuildTestDnsResponse(
+            base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
+            {{std::accumulate(audit_proof.begin() + nodes_begin,
+                              audit_proof.begin() + nodes_end,
+                              std::string())}})),
+        false /* delay */
+    );
+  }
+
+  std::unique_ptr<LogDnsClient> log_client =
+      CreateRuleBasedLogDnsClient(std::move(rules));
+
+  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
+  net::TestCompletionCallback callback;
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          false /* lookup_securely */, 999999,
+                                          &query, callback.callback()),
+              IsError(net::ERR_IO_PENDING));
+  EXPECT_THAT(callback.WaitForResult(), IsError(net::ERR_NAME_NOT_RESOLVED));
+
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          true /* lookup_securely */, 999999,
+                                          &query, callback.callback()),
+              IsError(net::ERR_IO_PENDING));
+  EXPECT_THAT(callback.WaitForResult(), IsOk());
+}
+
+TEST_P(LogDnsClientTest, SecureDnsMode_Insecure) {
+  const std::vector<std::string> audit_proof = GetSampleAuditProof(20);
+
+  net::MockDnsClientRuleList rules;
+  // Make leaf index queries for kLeafIndexQnames[0] successful only when
+  // lookup_securely is false.
+  rules.emplace_back(kLeafIndexQnames[0], net::dns_protocol::kTypeTXT,
+                     net::SecureDnsMode::OFF,
+                     net::MockDnsClientRule::Result(net::BuildTestDnsResponse(
+                         kLeafIndexQnames[0],
+                         std::vector<std::vector<std::string>>({{"123456"}}))),
+                     false /* delay */);
+
+  // Add successful audit proof queries for lookup_securely false.
+  for (size_t nodes_begin = 0; nodes_begin < audit_proof.size();
+       nodes_begin += kMaxProofNodesPerDnsResponse) {
+    const size_t nodes_end = std::min(
+        nodes_begin + kMaxProofNodesPerDnsResponse, audit_proof.size());
+    rules.emplace_back(
+        base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
+        net::dns_protocol::kTypeTXT, net::SecureDnsMode::OFF,
+        net::MockDnsClientRule::Result(net::BuildTestDnsResponse(
+            base::StringPrintf("%zu.123456.999999.tree.ct.test.", nodes_begin),
+            {{std::accumulate(audit_proof.begin() + nodes_begin,
+                              audit_proof.begin() + nodes_end,
+                              std::string())}})),
+        false /* delay */
+    );
+  }
+
+  std::unique_ptr<LogDnsClient> log_client =
+      CreateRuleBasedLogDnsClient(std::move(rules));
+
+  std::unique_ptr<LogDnsClient::AuditProofQuery> query;
+  net::TestCompletionCallback callback;
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          false /* lookup_securely */, 999999,
+                                          &query, callback.callback()),
+              IsError(net::ERR_IO_PENDING));
+  EXPECT_THAT(callback.WaitForResult(), IsOk());
+
+  ASSERT_THAT(log_client->QueryAuditProof("ct.test", kLeafHashes[0],
+                                          true /* lookup_securely */, 999999,
+                                          &query, callback.callback()),
+              IsError(net::ERR_IO_PENDING));
+  EXPECT_THAT(callback.WaitForResult(), IsError(net::ERR_NAME_NOT_RESOLVED));
+}
+
 INSTANTIATE_TEST_SUITE_P(ReadMode,
                          LogDnsClientTest,
                          ::testing::Values(net::IoMode::ASYNC,
diff --git a/components/certificate_transparency/single_tree_tracker.cc b/components/certificate_transparency/single_tree_tracker.cc
index b20bc9e..854c0890 100644
--- a/components/certificate_transparency/single_tree_tracker.cc
+++ b/components/certificate_transparency/single_tree_tracker.cc
@@ -159,8 +159,10 @@
 struct SingleTreeTracker::EntryToAudit {
   base::Time sct_timestamp;
   SHA256HashValue leaf_hash;
+  bool lookup_securely;
 
-  explicit EntryToAudit(base::Time timestamp) : sct_timestamp(timestamp) {}
+  explicit EntryToAudit(base::Time timestamp, bool lookup_securely)
+      : sct_timestamp(timestamp), lookup_securely(lookup_securely) {}
 };
 
 // State of a log entry: its audit state and information necessary to
@@ -211,7 +213,9 @@
 
 // Orders entries by the SCT timestamp. In case of tie, which is very unlikely
 // as it requires two SCTs issued from a log at exactly the same time, order
-// by leaf hash.
+// by leaf hash. There should never be multiple entries that are identical
+// apart from the |lookup_securely| field, so this field can be excluded from
+// the comparator.
 bool SingleTreeTracker::OrderByTimestamp::operator()(
     const EntryToAudit& lhs,
     const EntryToAudit& rhs) const {
@@ -255,19 +259,21 @@
   // It's ok to do this now, even though the inclusion check may not happen for
   // some time, because SingleTreeTracker will discard the SCT if the network
   // changes.
-  if (!WasLookedUpOverDNS(hostname)) {
+  bool secure;
+  if (!WasLookedUpOverDNS(hostname, &secure)) {
     LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_NO_DNS_LOOKUP);
     return;
   }
 
-  EntryToAudit entry(sct->timestamp);
+  EntryToAudit entry(sct->timestamp, secure /* lookup_securely */);
   if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash)) {
     LogCanBeCheckedForInclusionToUMA(NOT_AUDITED_INVALID_LEAF_HASH);
     return;
   }
 
-  // Avoid queueing multiple instances of the same entry.
-  switch (GetAuditedEntryInclusionStatus(entry)) {
+  // Avoid queueing multiple instances of the same entry, ignoring the value of
+  // the |lookup_securely| field.
+  switch (GetAuditedEntryInclusionStatus(entry, nullptr)) {
     case SCT_NOT_OBSERVED:
       // No need to record UMA, will be done below.
       break;
@@ -379,13 +385,14 @@
 }
 
 SingleTreeTracker::SCTInclusionStatus
-SingleTreeTracker::GetLogEntryInclusionStatus(
+SingleTreeTracker::GetLogEntryInclusionStatusForTesting(
     net::X509Certificate* cert,
-    const SignedCertificateTimestamp* sct) {
-  EntryToAudit entry(sct->timestamp);
+    const SignedCertificateTimestamp* sct,
+    bool* pending_lookup_securely) {
+  EntryToAudit entry(sct->timestamp, false /* lookup_securely */);
   if (!GetLogEntryLeafHash(cert, sct, &entry.leaf_hash))
     return SCT_NOT_OBSERVED;
-  return GetAuditedEntryInclusionStatus(entry);
+  return GetAuditedEntryInclusionStatus(entry, pending_lookup_securely);
 }
 
 void SingleTreeTracker::ProcessPendingEntries() {
@@ -401,8 +408,8 @@
         reinterpret_cast<const char*>(it->first.leaf_hash.data),
         crypto::kSHA256Length);
     net::Error result = dns_client_->QueryAuditProof(
-        ct_log_->dns_domain(), leaf_hash, verified_sth_.tree_size,
-        &(it->second.audit_proof_query),
+        ct_log_->dns_domain(), leaf_hash, it->first.lookup_securely,
+        verified_sth_.tree_size, &(it->second.audit_proof_query),
         base::Bind(&SingleTreeTracker::OnAuditProofObtained,
                    base::Unretained(this), it->first));
     // Handling proofs returned synchronously is not implemeted.
@@ -441,7 +448,9 @@
 }
 
 SingleTreeTracker::SCTInclusionStatus
-SingleTreeTracker::GetAuditedEntryInclusionStatus(const EntryToAudit& entry) {
+SingleTreeTracker::GetAuditedEntryInclusionStatus(
+    const EntryToAudit& entry,
+    bool* pending_lookup_securely) {
   const auto checked_entries_iterator = checked_entries_.Get(entry.leaf_hash);
   if (checked_entries_iterator != checked_entries_.end()) {
     return SCT_INCLUDED_IN_LOG;
@@ -451,7 +460,9 @@
   if (pending_iterator == pending_entries_.end()) {
     return SCT_NOT_OBSERVED;
   }
-
+  // Found match in |pending_entries_|, so set |pending_lookup_securely|.
+  if (pending_lookup_securely)
+    *pending_lookup_securely = pending_iterator->first.lookup_securely;
   switch (pending_iterator->second.state) {
     case PENDING_NEWER_STH:
       return SCT_PENDING_NEWER_STH;
@@ -525,10 +536,11 @@
                     net_log_callback);
 }
 
-bool SingleTreeTracker::WasLookedUpOverDNS(base::StringPiece hostname) const {
+bool SingleTreeTracker::WasLookedUpOverDNS(base::StringPiece hostname,
+                                           bool* secure) const {
   net::HostCache::Entry::Source source;
   net::HostCache::EntryStaleness staleness;
-  return host_resolver_->HasCached(hostname, &source, &staleness) &&
+  return host_resolver_->HasCached(hostname, &source, &staleness, secure) &&
          source == net::HostCache::Entry::SOURCE_DNS &&
          staleness.network_changes == 0;
 }
diff --git a/components/certificate_transparency/single_tree_tracker.h b/components/certificate_transparency/single_tree_tracker.h
index fa6b9d1e..1beffdb2 100644
--- a/components/certificate_transparency/single_tree_tracker.h
+++ b/components/certificate_transparency/single_tree_tracker.h
@@ -16,6 +16,7 @@
 #include "net/base/hash_value.h"
 #include "net/base/network_change_notifier.h"
 #include "net/cert/signed_tree_head.h"
+#include "net/dns/host_cache.h"
 #include "net/log/net_log_with_source.h"
 
 namespace net {
@@ -153,13 +154,15 @@
   // Must only be called for STHs issued by the log this instance tracks.
   void NewSTHObserved(const net::ct::SignedTreeHead& sth);
 
-  // Returns the status of a given log entry that is assembled from
-  // |cert| and |sct|. If |cert| and |sct| were not previously observed,
-  // |sct| is not an SCT for |cert| or |sct| is not for this log,
-  // SCT_NOT_OBSERVED will be returned.
-  SCTInclusionStatus GetLogEntryInclusionStatus(
+  // Returns the status of a given log entry that is assembled from |cert| and
+  // |sct|. If |cert| and |sct| were not previously observed, |sct| is not an
+  // SCT for |cert| or |sct| is not for this log, and SCT_NOT_OBSERVED will be
+  // returned. If the assembled entry is pending, |pending_lookup_securely| will
+  // be set to the value of the pending match's |lookup_securely| field.
+  SCTInclusionStatus GetLogEntryInclusionStatusForTesting(
       net::X509Certificate* cert,
-      const net::ct::SignedCertificateTimestamp* sct);
+      const net::ct::SignedCertificateTimestamp* sct,
+      bool* pending_lookup_securely = nullptr);
 
  private:
   struct EntryToAudit;
@@ -180,8 +183,12 @@
 
   // Returns the inclusion status of the given |entry|, similar to
   // GetLogEntryInclusionStatus(). The |entry| is an internal representation of
-  // a certificate + SCT combination.
-  SCTInclusionStatus GetAuditedEntryInclusionStatus(const EntryToAudit& entry);
+  // a certificate + SCT combination, and the |lookup_securely| value in |entry|
+  // is ignored. If |entry| has a pending status, |pending_lookup_securely| will
+  // be set to the value fo the pending match's |lookup_securely| field.
+  SCTInclusionStatus GetAuditedEntryInclusionStatus(
+      const EntryToAudit& entry,
+      bool* pending_lookup_securely);
 
   // Processes the result of obtaining an audit proof for |entry|.
   // * If an audit proof was successfully obtained and validated,
@@ -208,7 +215,7 @@
 
   // Returns true if |hostname| has previously been looked up using DNS, and the
   // network has not changed since.
-  bool WasLookedUpOverDNS(base::StringPiece hostname) const;
+  bool WasLookedUpOverDNS(base::StringPiece hostname, bool* secure) const;
 
   // Holds the latest STH fetched and verified for this log.
   net::ct::SignedTreeHead verified_sth_;
diff --git a/components/certificate_transparency/single_tree_tracker_unittest.cc b/components/certificate_transparency/single_tree_tracker_unittest.cc
index 00a5f13..39ab151 100644
--- a/components/certificate_transparency/single_tree_tracker_unittest.cc
+++ b/components/certificate_transparency/single_tree_tracker_unittest.cc
@@ -176,10 +176,12 @@
 
 void AddCacheEntry(net::HostCache* cache,
                    const std::string& hostname,
+                   bool secure,
                    net::HostCache::Entry::Source source,
                    base::TimeDelta ttl) {
-  cache->Set(net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0),
-             net::HostCache::Entry(net::OK, net::AddressList(), source),
+  auto key = net::HostCache::Key(hostname, net::ADDRESS_FAMILY_UNSPECIFIED, 0);
+  key.secure = secure;
+  cache->Set(key, net::HostCache::Entry(net::OK, net::AddressList(), source),
              base::TimeTicks::Now(), ttl);
 }
 
@@ -298,7 +300,7 @@
 // the user had visited that host.
 TEST_F(SingleTreeTrackerTest, DiscardsSCTWhenHostnameNotLookedUpUsingDNS) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_UNKNOWN, kZeroTTL);
 
   base::HistogramTester histograms;
@@ -308,16 +310,16 @@
   tree_tracker_->NewSTHObserved(sth);
 
   // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
 
   // The status for this SCT should still be 'not observed'.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Exactly one value should be logged, indicating that the SCT could not be
   // checked for inclusion because of no prior DNS lookup for this hostname.
@@ -342,16 +344,16 @@
   tree_tracker_->NewSTHObserved(sth);
 
   // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   tree_tracker_->OnSCTVerified("::1", chain_.get(), cert_sct_.get());
 
   // The status for this SCT should still be 'not observed'.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Exactly one value should be logged, indicating that the SCT could not be
   // checked for inclusion because of no prior DNS lookup for this hostname
@@ -373,7 +375,7 @@
 TEST_F(SingleTreeTrackerTest,
        DiscardsSCTWhenHostnameLookedUpUsingDNSOnDiffNetwork) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   // Simulate network change.
@@ -386,16 +388,16 @@
   tree_tracker_->NewSTHObserved(sth);
 
   // Make sure the SCT status is the same as if there's no STH for this log.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
 
   // The status for this SCT should still be 'not observed'.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Exactly one value should be logged, indicating that the SCT could not be
   // checked for inclusion because of no prior DNS lookup for this hostname on
@@ -408,26 +410,83 @@
   EXPECT_EQ(0u, net_log_.GetSize());
 }
 
+TEST_F(SingleTreeTrackerTest, EntriesIndistinguishedBySecurity) {
+  CreateTreeTrackerWithDefaultDnsExpectation();
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
+                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, true /* secure */,
+                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
+
+  base::HistogramTester histograms;
+  // Provide an STH to the tree_tracker_.
+  SignedTreeHead sth;
+  GetSampleSignedTreeHead(&sth);
+  tree_tracker_->NewSTHObserved(sth);
+
+  // Make sure the SCT status is the same as if there's no STH for this log.
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
+
+  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
+
+  // The status for this SCT should be 'pending inclusion check' with
+  // |pending_lookup_securely| set to true since the cache check will return the
+  // secure key.
+  bool pending_lookup_securely;
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get(), &pending_lookup_securely));
+  EXPECT_TRUE(pending_lookup_securely);
+
+  // Exactly one value should be logged, indicating the SCT can be checked for
+  // inclusion, as |tree_tracker_| did have a valid STH when it was notified
+  // of a new SCT.
+  histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 2, 1);
+
+  // Simulate network change.
+  host_resolver_.GetHostCache()->OnNetworkChange();
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
+                net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
+
+  tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
+
+  // The status for this SCT should still be 'pending inclusion check' with
+  // |pending_lookup_securely| set to true.
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get(), &pending_lookup_securely));
+  EXPECT_TRUE(pending_lookup_securely);
+
+  // Another value should be logged, indicating that there is already a
+  // pending audit check for this SCT.
+  histograms.ExpectBucketCount(kCanCheckForInclusionHistogramName, 6, 1);
+  // Nothing should be logged in the result histogram or net log since an
+  // inclusion check wasn't performed.
+  histograms.ExpectTotalCount(kInclusionCheckResultHistogramName, 0);
+  EXPECT_EQ(0u, net_log_.GetSize());
+}
+
 // Test that an SCT is classified as pending for a newer STH if the
 // SingleTreeTracker has not seen any STHs so far.
 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTNoSTH) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   base::HistogramTester histograms;
   // First make sure the SCT has not been observed at all.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
 
   // Since no STH was provided to the tree_tracker_ the status should be that
   // the SCT is pending a newer STH.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Expect logging of a value indicating a valid STH is required.
   histograms.ExpectUniqueSample(kCanCheckForInclusionHistogramName, 0, 1);
@@ -438,7 +497,7 @@
 // SingleTreeTracker has a fresh-enough STH to check inclusion against.
 TEST_F(SingleTreeTrackerTest, CorrectlyClassifiesUnobservedSCTWithRecentSTH) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   base::HistogramTester histograms;
@@ -449,17 +508,17 @@
 
   // Make sure the SCT status is the same as if there's no STH for
   // this log.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
 
   // The status for this SCT should be 'pending inclusion check' since the STH
   // provided at the beginning of the test is newer than the SCT.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Exactly one value should be logged, indicating the SCT can be checked for
   // inclusion, as |tree_tracker_| did have a valid STH when it was notified
@@ -476,16 +535,16 @@
 // from pending newer STH to pending inclusion check.
 TEST_F(SingleTreeTrackerTest, CorrectlyUpdatesSCTStatusOnNewSTH) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   base::HistogramTester histograms;
   // Report an observed SCT and make sure it's in the pending newer STH
   // state.
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
   histograms.ExpectTotalCount(kCanCheckForInclusionHistogramName, 1);
 
   // Provide with a fresh STH
@@ -494,9 +553,9 @@
   tree_tracker_->NewSTHObserved(sth);
 
   // Test that its status has changed.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
   // Check that no additional UMA was logged for this case as the histogram is
   // only supposed to measure the state of newly-observed SCTs, not pending
   // ones.
@@ -509,14 +568,14 @@
 // inclusion against.
 TEST_F(SingleTreeTrackerTest, DoesNotUpdatesSCTStatusOnOldSTH) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   // Notify of an SCT and make sure it's in the 'pending newer STH' state.
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Provide an old STH for the same log.
   SignedTreeHead sth;
@@ -524,9 +583,9 @@
   tree_tracker_->NewSTHObserved(sth);
 
   // Make sure the SCT's state hasn't changed.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
   EXPECT_EQ(0u, net_log_.GetSize());
 }
 
@@ -535,7 +594,7 @@
 // STH.
 TEST_F(SingleTreeTrackerTest, LogsUMAForNewSCTAndOldSTH) {
   CreateTreeTrackerWithDefaultDnsExpectation();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   base::HistogramTester histograms;
@@ -564,13 +623,13 @@
       net::Error::ERR_FAILED));
 
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Provide with a fresh STH
   SignedTreeHead sth;
@@ -578,9 +637,9 @@
   tree_tracker_->NewSTHObserved(sth);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
   // There should have been one NetLog event, logged with failure.
   EXPECT_TRUE(MatchAuditingResultInNetLog(
       net_log_, LeafHash(chain_.get(), cert_sct_.get()), false));
@@ -602,13 +661,13 @@
       net::Error::ERR_FAILED));
 
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Provide with a fresh STH
   SignedTreeHead sth;
@@ -616,9 +675,9 @@
   tree_tracker_->NewSTHObserved(sth);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
   // There should have been one NetLog event, logged with failure.
   EXPECT_TRUE(MatchAuditingResultInNetLog(
       net_log_, LeafHash(chain_.get(), cert_sct_.get()), false));
@@ -644,13 +703,13 @@
       audit_proof.begin() + 1));
 
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Provide with a fresh STH, which is for a tree of size 2.
   SignedTreeHead sth;
@@ -660,9 +719,9 @@
   tree_tracker_->NewSTHObserved(sth);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_INCLUDED_IN_LOG,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_INCLUDED_IN_LOG,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
   // There should have been one NetLog event, with success logged.
   EXPECT_TRUE(MatchAuditingResultInNetLog(
       net_log_, LeafHash(chain_.get(), cert_sct_.get()), true));
@@ -673,13 +732,13 @@
 TEST_F(SingleTreeTrackerTest,
        TestInclusionCheckCancelledIfUnderMemoryPressure) {
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), cert_sct_.get());
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_PENDING_NEWER_STH,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Provide with a fresh STH, which is for a tree of size 2.
   SignedTreeHead sth;
@@ -702,9 +761,9 @@
   base::RunLoop().RunUntilIdle();
 
   // Expect the SCT to have been discarded.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 }
 
 // Test that pending entries transition states correctly according to the
@@ -764,7 +823,7 @@
   ASSERT_TRUE(
       ExpectLeafIndexRequestAndThrottle(chain_, newer_than_old_sth_sct));
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   // Add SCTs in mixed order.
@@ -782,9 +841,9 @@
   for (const auto& sct :
        {oldest_sct, not_auditable_by_old_sth_sct, newer_than_old_sth_sct,
         not_auditable_by_new_sth_sct, newer_than_new_sth_sct}) {
-    ASSERT_EQ(
-        SingleTreeTracker::SCT_PENDING_NEWER_STH,
-        tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), sct.get()))
+    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
+                                                                  sct.get()))
         << "SCT age: " << sct->timestamp;
   }
 
@@ -792,15 +851,15 @@
   tree_tracker_->NewSTHObserved(old_sth);
   // Ensure all but the oldest are in the PENDING_NEWER_STH state.
   ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
-                                                      oldest_sct.get()));
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), oldest_sct.get()));
 
   for (const auto& sct :
        {not_auditable_by_old_sth_sct, newer_than_old_sth_sct,
         not_auditable_by_new_sth_sct, newer_than_new_sth_sct}) {
-    ASSERT_EQ(
-        SingleTreeTracker::SCT_PENDING_NEWER_STH,
-        tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), sct.get()))
+    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
+                                                                  sct.get()))
         << "SCT age: " << sct->timestamp;
   }
 
@@ -810,17 +869,17 @@
 
   for (const auto& sct :
        {not_auditable_by_old_sth_sct, newer_than_old_sth_sct}) {
-    ASSERT_EQ(
-        SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-        tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), sct.get()))
+    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
+                                                                  sct.get()))
         << "SCT age: " << sct->timestamp;
   }
 
   for (const auto& sct :
        {not_auditable_by_new_sth_sct, newer_than_new_sth_sct}) {
-    ASSERT_EQ(
-        SingleTreeTracker::SCT_PENDING_NEWER_STH,
-        tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), sct.get()))
+    ASSERT_EQ(SingleTreeTracker::SCT_PENDING_NEWER_STH,
+              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
+                                                                  sct.get()))
         << "SCT age: " << sct->timestamp;
   }
 }
@@ -855,7 +914,7 @@
       audit_proof.begin() + 1));
 
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   SignedTreeHead sth;
@@ -868,27 +927,27 @@
   // Both entries should be in the pending state, the first because the
   // LogDnsClient did not invoke the callback yet, the second one because
   // the LogDnsClient is "busy" with the first entry and so would throttle.
-  ASSERT_EQ(
-      SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
   ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-            tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
-                                                      second_sct.get()));
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
+  ASSERT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), second_sct.get()));
 
   // Process pending DNS queries so later assertions are on handling
   // of the entries based on replies received.
   base::RunLoop().RunUntilIdle();
 
   // Check that the first sct is included in the log.
-  ASSERT_EQ(
-      SingleTreeTracker::SCT_INCLUDED_IN_LOG,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  ASSERT_EQ(SingleTreeTracker::SCT_INCLUDED_IN_LOG,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Check that the second SCT got an invalid proof and is not included, rather
   // than being in the pending state.
   ASSERT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
-            tree_tracker_->GetLogEntryInclusionStatus(chain_.get(),
-                                                      second_sct.get()));
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), second_sct.get()));
 }
 
 // Test that proof fetching failure due to DNS config errors is handled
@@ -908,7 +967,7 @@
   net_change_notifier_ =
       base::WrapUnique(net::NetworkChangeNotifier::CreateMock());
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   tree_tracker_->NewSTHObserved(sth);
@@ -917,9 +976,9 @@
   // Make sure the SCT status indicates the entry has been removed from
   // the SingleTreeTracker's internal queue as the DNS lookup failed
   // synchronously.
-  EXPECT_EQ(
-      SingleTreeTracker::SCT_NOT_OBSERVED,
-      tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), cert_sct_.get()));
+  EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+            tree_tracker_->GetLogEntryInclusionStatusForTesting(
+                chain_.get(), cert_sct_.get()));
 
   // Exactly one value should be logged, indicating the SCT can be checked for
   // inclusion, as |tree_tracker_| did have a valid STH when it was notified
@@ -959,7 +1018,7 @@
       audit_proof.begin() + 1));
 
   CreateTreeTracker();
-  AddCacheEntry(host_resolver_.GetHostCache(), kHostname,
+  AddCacheEntry(host_resolver_.GetHostCache(), kHostname, false /* secure */,
                 net::HostCache::Entry::SOURCE_DNS, kZeroTTL);
 
   // Provide an STH to the tree_tracker_.
@@ -971,9 +1030,9 @@
   tree_tracker_->OnSCTVerified(kHostname, chain_.get(), second_sct.get());
 
   for (auto sct : {cert_sct_, second_sct}) {
-    EXPECT_EQ(
-        SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
-        tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), sct.get()));
+    EXPECT_EQ(SingleTreeTracker::SCT_PENDING_INCLUSION_CHECK,
+              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
+                                                                  sct.get()));
   }
 
   net_change_notifier_->NotifyObserversOfNetworkChangeForTests(
@@ -981,9 +1040,9 @@
   base::RunLoop().RunUntilIdle();
 
   for (auto sct : {cert_sct_, second_sct}) {
-    EXPECT_EQ(
-        SingleTreeTracker::SCT_NOT_OBSERVED,
-        tree_tracker_->GetLogEntryInclusionStatus(chain_.get(), sct.get()));
+    EXPECT_EQ(SingleTreeTracker::SCT_NOT_OBSERVED,
+              tree_tracker_->GetLogEntryInclusionStatusForTesting(chain_.get(),
+                                                                  sct.get()));
   }
 }
 
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index 9b632fec..303e7728 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -444,11 +444,12 @@
       optional_parameters.value_or(ResolveHostParameters()), tick_clock_);
 }
 
-bool StaleHostResolver::HasCached(
-    base::StringPiece hostname,
-    net::HostCache::Entry::Source* source_out,
-    net::HostCache::EntryStaleness* stale_out) const {
-  return inner_resolver_->HasCached(hostname, source_out, stale_out);
+bool StaleHostResolver::HasCached(base::StringPiece hostname,
+                                  net::HostCache::Entry::Source* source_out,
+                                  net::HostCache::EntryStaleness* stale_out,
+                                  bool* secure_out) const {
+  return inner_resolver_->HasCached(hostname, source_out, stale_out,
+                                    secure_out);
 }
 
 void StaleHostResolver::SetDnsClientEnabled(bool enabled) {
diff --git a/components/cronet/stale_host_resolver.h b/components/cronet/stale_host_resolver.h
index 7e9c8f5..3f82a9d 100644
--- a/components/cronet/stale_host_resolver.h
+++ b/components/cronet/stale_host_resolver.h
@@ -85,7 +85,8 @@
   net::HostCache* GetHostCache() override;
   bool HasCached(base::StringPiece hostname,
                  net::HostCache::Entry::Source* source_out,
-                 net::HostCache::EntryStaleness* stale_out) const override;
+                 net::HostCache::EntryStaleness* stale_out,
+                 bool* secure_out) const override;
   std::unique_ptr<base::Value> GetDnsConfigAsValue() const override;
 
  private:
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index 99b2b508..faa2e152 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -93,11 +93,11 @@
 
   net::MockDnsClientRuleList rules;
   rules.emplace_back(
-      kHostname, net::dns_protocol::kTypeA,
+      kHostname, net::dns_protocol::kTypeA, net::SecureDnsMode::AUTOMATIC,
       net::MockDnsClientRule::Result(net::MockDnsClientRule::FAIL),
       true /* delay */);
   rules.emplace_back(
-      kHostname, net::dns_protocol::kTypeAAAA,
+      kHostname, net::dns_protocol::kTypeAAAA, net::SecureDnsMode::AUTOMATIC,
       net::MockDnsClientRule::Result(net::MockDnsClientRule::FAIL),
       true /* delay */);
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 15b1c88..d120f02 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -14651,7 +14651,10 @@
     {
       'name': 'PluginVmLicenseKey',
       'type': 'string',
-      'schema': { 'type': 'string' },
+      'schema': {
+        'sensitiveValue': True,
+        'type': 'string'
+      },
       'supported_on': ['chrome_os:73-'],
       'device_only': True,
       'features': {
diff --git a/components/signin/core/browser/account_fetcher_service.cc b/components/signin/core/browser/account_fetcher_service.cc
index 45c022a..687e5c7b 100644
--- a/components/signin/core/browser/account_fetcher_service.cc
+++ b/components/signin/core/browser/account_fetcher_service.cc
@@ -99,7 +99,7 @@
   return user_info_requests_.empty();
 }
 
-void AccountFetcherService::FetchUserInfoBeforeSignin(
+void AccountFetcherService::ForceRefreshOfAccountInfo(
     const std::string& account_id) {
   DCHECK(network_fetches_enabled_);
   RefreshAccountInfo(account_id, false);
diff --git a/components/signin/core/browser/account_fetcher_service.h b/components/signin/core/browser/account_fetcher_service.h
index f6a61ca..7d36bd86 100644
--- a/components/signin/core/browser/account_fetcher_service.h
+++ b/components/signin/core/browser/account_fetcher_service.h
@@ -66,7 +66,7 @@
   // there are still unfininshed fetchers.
   virtual bool IsAllUserInfoFetched() const;
 
-  void FetchUserInfoBeforeSignin(const std::string& account_id);
+  void ForceRefreshOfAccountInfo(const std::string& account_id);
 
   AccountTrackerService* account_tracker_service() const {
     return account_tracker_service_;
diff --git a/components/signin/core/browser/fake_account_fetcher_service.cc b/components/signin/core/browser/fake_account_fetcher_service.cc
index 72e4df83..c70081c 100644
--- a/components/signin/core/browser/fake_account_fetcher_service.cc
+++ b/components/signin/core/browser/fake_account_fetcher_service.cc
@@ -5,6 +5,7 @@
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
 
 #include "base/values.h"
+#include "build/build_config.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "ui/gfx/image/image_unittest_util.h"
@@ -16,6 +17,13 @@
   // In tests, don't do actual network fetch.
 }
 
+#if defined(OS_ANDROID)
+void FakeAccountFetcherService::StartFetchingChildInfo(
+    const std::string& account_id) {
+  // In tests, don't do actual network fetch.
+}
+#endif
+
 TestImageDecoder::TestImageDecoder() = default;
 
 TestImageDecoder::~TestImageDecoder() = default;
diff --git a/components/signin/core/browser/fake_account_fetcher_service.h b/components/signin/core/browser/fake_account_fetcher_service.h
index a57cfd2..5f56123 100644
--- a/components/signin/core/browser/fake_account_fetcher_service.h
+++ b/components/signin/core/browser/fake_account_fetcher_service.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "components/image_fetcher/core/image_decoder.h"
 #include "components/signin/core/browser/account_fetcher_service.h"
 
@@ -22,6 +23,9 @@
 
  private:
   void StartFetchingUserInfo(const std::string& account_id) override;
+#if defined(OS_ANDROID)
+  void StartFetchingChildInfo(const std::string& account_id) override;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(FakeAccountFetcherService);
 };
diff --git a/components/sync/base/sync_prefs.cc b/components/sync/base/sync_prefs.cc
index 5b05045..a1aa08c 100644
--- a/components/sync/base/sync_prefs.cc
+++ b/components/sync/base/sync_prefs.cc
@@ -63,9 +63,6 @@
   pref_groups[PROXY_TABS].Put(FAVICON_IMAGES);
   pref_groups[PROXY_TABS].Put(FAVICON_TRACKING);
 
-  // TODO(zea): Put favicons in the bookmarks group as well once it handles
-  // those favicons.
-
   return pref_groups;
 }
 
@@ -142,51 +139,48 @@
 // static
 void SyncPrefs::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterStringPref(prefs::kSyncCacheGuid, std::string());
-  registry->RegisterStringPref(prefs::kSyncBirthday, std::string());
-  registry->RegisterStringPref(prefs::kSyncBagOfChips, std::string());
+  // Actual user-controlled preferences.
   registry->RegisterBooleanPref(prefs::kSyncFirstSetupComplete, false);
   registry->RegisterBooleanPref(prefs::kSyncSuppressStart, false);
-  registry->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
-  registry->RegisterInt64Pref(prefs::kSyncLastPollTime, 0);
-  registry->RegisterInt64Pref(prefs::kSyncFirstSyncTime, 0);
-  registry->RegisterInt64Pref(prefs::kSyncShortPollIntervalSeconds, 0);
-  registry->RegisterInt64Pref(prefs::kSyncLongPollIntervalSeconds, 0);
-
-  // All datatypes are on by default, but this gets set explicitly
-  // when you configure sync (when turning it on), in
-  // ProfileSyncService::OnUserChoseDatatypes.
   registry->RegisterBooleanPref(prefs::kSyncKeepEverythingSynced, true);
-
-  RegisterObsoleteUserTypePrefs(registry);
-
-  // All types are set to off by default, which forces a configuration to
-  // explicitly enable them.
   for (ModelType type : UserSelectableTypes()) {
     RegisterDataTypePreferredPref(registry, type);
   }
 
+  // Internal or bookkeeping prefs.
+  registry->RegisterStringPref(prefs::kSyncCacheGuid, std::string());
+  registry->RegisterStringPref(prefs::kSyncBirthday, std::string());
+  registry->RegisterStringPref(prefs::kSyncBagOfChips, std::string());
+  registry->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
+  registry->RegisterInt64Pref(prefs::kSyncLastPollTime, 0);
+  // TODO(crbug.com/938865): Remove this pref.
+  registry->RegisterInt64Pref(prefs::kSyncFirstSyncTime, 0);
+  registry->RegisterInt64Pref(prefs::kSyncShortPollIntervalSeconds, 0);
+  registry->RegisterInt64Pref(prefs::kSyncLongPollIntervalSeconds, 0);
   registry->RegisterBooleanPref(prefs::kSyncManaged, false);
   registry->RegisterStringPref(prefs::kSyncEncryptionBootstrapToken,
                                std::string());
   registry->RegisterStringPref(prefs::kSyncKeystoreEncryptionBootstrapToken,
                                std::string());
 #if defined(OS_CHROMEOS)
+  // TODO(crbug.com/938869): Remove this pref.
   registry->RegisterStringPref(prefs::kSyncSpareBootstrapToken, "");
 #endif
-
-  registry->RegisterBooleanPref(kSyncHasAuthError, false);
   registry->RegisterBooleanPref(prefs::kSyncPassphrasePrompted, false);
   registry->RegisterIntegerPref(prefs::kSyncMemoryPressureWarningCount, -1);
   registry->RegisterBooleanPref(prefs::kSyncShutdownCleanly, false);
   registry->RegisterDictionaryPref(prefs::kSyncInvalidationVersions);
   registry->RegisterStringPref(prefs::kSyncLastRunVersion, std::string());
+  registry->RegisterBooleanPref(prefs::kEnableLocalSyncBackend, false);
+  registry->RegisterFilePathPref(prefs::kLocalSyncBackendDir, base::FilePath());
+
+  // Obsolete prefs that will be removed after a grace period.
+  RegisterObsoleteUserTypePrefs(registry);
   registry->RegisterBooleanPref(kSyncPassphraseEncryptionTransitionInProgress,
                                 false);
   registry->RegisterStringPref(kSyncNigoriStateForPassphraseTransition,
                                std::string());
-  registry->RegisterBooleanPref(prefs::kEnableLocalSyncBackend, false);
-  registry->RegisterFilePathPref(prefs::kLocalSyncBackendDir, base::FilePath());
+  registry->RegisterBooleanPref(kSyncHasAuthError, false);
 }
 
 void SyncPrefs::AddSyncPrefObserver(SyncPrefObserver* sync_pref_observer) {
@@ -201,6 +195,7 @@
 
 void SyncPrefs::ClearPreferences() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   pref_service_->ClearPref(prefs::kSyncCacheGuid);
   pref_service_->ClearPref(prefs::kSyncBirthday);
   pref_service_->ClearPref(prefs::kSyncBagOfChips);
@@ -208,7 +203,6 @@
   pref_service_->ClearPref(prefs::kSyncLastPollTime);
   pref_service_->ClearPref(prefs::kSyncShortPollIntervalSeconds);
   pref_service_->ClearPref(prefs::kSyncLongPollIntervalSeconds);
-  pref_service_->ClearPref(prefs::kSyncFirstSetupComplete);
   pref_service_->ClearPref(prefs::kSyncEncryptionBootstrapToken);
   pref_service_->ClearPref(prefs::kSyncKeystoreEncryptionBootstrapToken);
   pref_service_->ClearPref(prefs::kSyncPassphrasePrompted);
@@ -216,9 +210,14 @@
   pref_service_->ClearPref(prefs::kSyncShutdownCleanly);
   pref_service_->ClearPref(prefs::kSyncInvalidationVersions);
   pref_service_->ClearPref(prefs::kSyncLastRunVersion);
+  // No need to clear kManaged, kEnableLocalSyncBackend or kLocalSyncBackendDir,
+  // since they're never actually set as user preferences.
 
   // Note: We do *not* clear prefs which are directly user-controlled such as
-  // the set of preferred data types here.
+  // the set of preferred data types here, so that if the user ever chooses to
+  // enable Sync again, they start off with their previous settings by default.
+  // We do however require going through first-time setup again.
+  pref_service_->ClearPref(prefs::kSyncFirstSetupComplete);
 }
 
 bool SyncPrefs::IsFirstSetupComplete() const {
diff --git a/components/sync/driver/startup_controller.cc b/components/sync/driver/startup_controller.cc
index 2b18a84..0b7da17 100644
--- a/components/sync/driver/startup_controller.cc
+++ b/components/sync/driver/startup_controller.cc
@@ -59,7 +59,7 @@
 
 StartupController::StartupController(
     base::RepeatingCallback<ModelTypeSet()> get_preferred_data_types,
-    base::RepeatingCallback<bool(bool)> should_start,
+    base::RepeatingCallback<bool()> should_start,
     base::RepeatingClosure start_engine)
     : get_preferred_data_types_callback_(std::move(get_preferred_data_types)),
       should_start_callback_(std::move(should_start)),
@@ -102,7 +102,7 @@
 }
 
 void StartupController::TryStart(bool force_immediate) {
-  if (!should_start_callback_.Run(force_immediate)) {
+  if (!should_start_callback_.Run()) {
     return;
   }
 
diff --git a/components/sync/driver/startup_controller.h b/components/sync/driver/startup_controller.h
index 9c02ff7..fe55df7 100644
--- a/components/sync/driver/startup_controller.h
+++ b/components/sync/driver/startup_controller.h
@@ -29,7 +29,7 @@
 
   StartupController(
       base::RepeatingCallback<ModelTypeSet()> get_preferred_data_types,
-      base::RepeatingCallback<bool(bool force_immediate)> should_start,
+      base::RepeatingCallback<bool()> should_start,
       base::RepeatingClosure start_engine);
   ~StartupController();
 
@@ -68,7 +68,7 @@
 
   // A function that can be invoked repeatedly to determine whether sync should
   // be started. |start_engine_| should not be invoked unless this returns true.
-  const base::RepeatingCallback<bool(bool)> should_start_callback_;
+  const base::RepeatingCallback<bool()> should_start_callback_;
 
   // The callback we invoke when it's time to call expensive
   // startup routines for the sync engine.
diff --git a/components/sync/driver/startup_controller_unittest.cc b/components/sync/driver/startup_controller_unittest.cc
index 752f78d6..996ab84 100644
--- a/components/sync/driver/startup_controller_unittest.cc
+++ b/components/sync/driver/startup_controller_unittest.cc
@@ -62,7 +62,7 @@
 
  private:
   ModelTypeSet GetPreferredDataTypes() { return preferred_types_; }
-  bool ShouldStart(bool force_immediate) { return should_start_; }
+  bool ShouldStart() { return should_start_; }
   void FakeStartBackend() { started_ = true; }
 
   ModelTypeSet preferred_types_;
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index 882962a..a3b4a73 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -73,11 +73,6 @@
 const base::Feature kSyncSendTabToSelf{"SyncSendTabToSelf",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 
-// If enabled, allows the Sync machinery ("transport layer") to start
-// independently of Sync-the-feature.
-const base::Feature kSyncStandaloneTransport{"SyncStandaloneTransport",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
-
 // If enabled, allows the Sync machinery to start with a signed-in account that
 // has *not* been chosen as Chrome's primary account (see IdentityManager). Only
 // has an effect if SyncStandaloneTransport is also enabled.
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index 5d09d6b..fc6f979 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -35,7 +35,6 @@
 extern const base::Feature kSyncPseudoUSSSupervisedUsers;
 extern const base::Feature kSyncPseudoUSSThemes;
 extern const base::Feature kSyncSendTabToSelf;
-extern const base::Feature kSyncStandaloneTransport;
 extern const base::Feature kSyncSupportSecondaryAccount;
 extern const base::Feature kSyncUserEvents;
 extern const base::Feature kSyncUserFieldTrialEvents;
diff --git a/components/variations/platform_field_trials.h b/components/variations/platform_field_trials.h
index 3a7f642..bb010fe 100644
--- a/components/variations/platform_field_trials.h
+++ b/components/variations/platform_field_trials.h
@@ -29,6 +29,10 @@
       bool has_seed,
       base::FeatureList* feature_list) = 0;
 
+  // Register any synthetic field trials. Will be called later than the above
+  // methods, in particular after g_browser_process is available..
+  virtual void RegisterSyntheticTrials() {}
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PlatformFieldTrials);
 };
diff --git a/components/viz/common/quads/draw_quad_perftest.cc b/components/viz/common/quads/draw_quad_perftest.cc
index 8e235328..6759fde2 100644
--- a/components/viz/common/quads/draw_quad_perftest.cc
+++ b/components/viz/common/quads/draw_quad_perftest.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "components/viz/common/quads/draw_quad.h"
 #include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
@@ -104,7 +104,7 @@
  private:
   std::unique_ptr<RenderPass> render_pass_;
   SharedQuadState* shared_state_;
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(DrawQuadPerfTest, IterateResources) {
diff --git a/components/viz/host/host_frame_sink_manager.cc b/components/viz/host/host_frame_sink_manager.cc
index 63d8a0d..57584df 100644
--- a/components/viz/host/host_frame_sink_manager.cc
+++ b/components/viz/host/host_frame_sink_manager.cc
@@ -305,6 +305,10 @@
 
 void HostFrameSinkManager::EvictSurfaces(
     const std::vector<SurfaceId>& surface_ids) {
+  // TODO(sgilhuly): Remove this check once crbug.com/911308 is fixed.
+  for (auto& surface_id : surface_ids) {
+    CHECK(surface_id.is_valid());
+  }
   frame_sink_manager_->EvictSurfaces(surface_ids);
 }
 
diff --git a/components/viz/service/display/bsp_tree_perftest.cc b/components/viz/service/display/bsp_tree_perftest.cc
index 29efb30..2895bd10 100644
--- a/components/viz/service/display/bsp_tree_perftest.cc
+++ b/components/viz/service/display/bsp_tree_perftest.cc
@@ -14,7 +14,7 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/layers/layer.h"
 #include "cc/test/fake_content_layer_client.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -135,12 +135,12 @@
   void AfterTest() override {
     CHECK(!test_name_.empty()) << "Must SetTestName() before TearDown().";
     perf_test::PrintResult("calc_draw_props_time", "", test_name_,
-                           1000 * timer_.MsPerLap(), "us", true);
+                           timer_.TimePerLap().InMicrosecondsF(), "us", true);
   }
 
  private:
   cc::FakeContentLayerClient content_layer_client_;
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
   std::string test_name_;
   std::string json_;
   cc::LayerImplList base_list_;
diff --git a/components/viz/service/display/display_perftest.cc b/components/viz/service/display/display_perftest.cc
index dc09344a..d6f91b7 100644
--- a/components/viz/service/display/display_perftest.cc
+++ b/components/viz/service/display/display_perftest.cc
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/test/null_task_runner.h"
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "components/viz/common/display/renderer_settings.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/draw_quad.h"
@@ -290,7 +290,7 @@
 
  private:
   CompositorFrame frame_;
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
   StubBeginFrameSource begin_frame_source_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   ServerSharedBitmapManager bitmap_manager_;
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index f749b54..c08fab0 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -71,6 +71,10 @@
 
 }  // namespace
 
+std::string SurfaceAggregator::ClipData::ToString() const {
+  return is_clipped ? "clip " + rect.ToString() : "no clip";
+}
+
 SurfaceAggregator::SurfaceAggregator(SurfaceManager* manager,
                                      DisplayResourceProvider* provider,
                                      bool aggregate_only_damaged)
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index 352a124c..d692e75 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_SURFACE_AGGREGATOR_H_
 
 #include <memory>
+#include <string>
 #include <unordered_map>
 
 #include "base/containers/flat_map.h"
@@ -64,6 +65,8 @@
     ClipData(bool is_clipped, const gfx::Rect& rect)
         : is_clipped(is_clipped), rect(rect) {}
 
+    std::string ToString() const;
+
     bool is_clipped;
     gfx::Rect rect;
   };
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index 2975828a..95539d5 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "cc/test/fake_output_surface_client.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame.h"
@@ -157,7 +157,7 @@
   scoped_refptr<TestContextProvider> context_provider_;
   std::unique_ptr<DisplayResourceProvider> resource_provider_;
   std::unique_ptr<SurfaceAggregator> aggregator_;
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(SurfaceAggregatorPerfTest, ManySurfacesOpaque) {
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index 6e14e2d..dc15ded 100644
--- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -120,6 +120,9 @@
   }
 
   FrameSinkManagerImpl& frame_sink_manager() { return frame_sink_manager_; }
+  SurfaceManager* surface_manager() {
+    return frame_sink_manager_.surface_manager();
+  }
 
   // Returns all the references where |surface_id| is the parent.
   const base::flat_set<SurfaceId>& GetChildReferences(
@@ -135,12 +138,6 @@
         surface_id);
   }
 
-  // Returns true if there is a Persistent reference for |surface_id|.
-  bool HasPersistentReference(const SurfaceId& surface_id) {
-    return frame_sink_manager().surface_manager()->HasPersistentReference(
-        surface_id);
-  }
-
   Surface* GetLatestInFlightSurface(const SurfaceRange& surface_range) {
     return frame_sink_manager().surface_manager()->GetLatestInFlightSurface(
         surface_range);
@@ -2058,9 +2055,11 @@
             GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3)));
 }
 
-// This test verifies that GetLatestInFlightSurface will return nullptr
-// if it has a bogus fallback SurfaceID.
-TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithBogusFallback) {
+// This test verifies that GetLatestInFlightSurface will return nullptr when the
+// start of the range is newer than its end, even if a surface matching the end
+// exists.
+TEST_F(SurfaceSynchronizationTest,
+       LatestInFlightSurfaceWithInvalidSurfaceRange) {
   const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
   const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1);
   const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2);
@@ -2084,12 +2083,11 @@
 
   const SurfaceId bogus_child_id = MakeSurfaceId(kChildFrameSink1, 10);
 
-  // If primary exists and active return it regardless of the fallback.
-  EXPECT_EQ(GetSurfaceForId(child_id1),
+  // The end exists but don't return it because the start is newer than the end.
+  EXPECT_EQ(nullptr,
             GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id1)));
 
-  // If primary is not active and fallback is doesn't exist, we always return
-  // nullptr.
+  // In this case, the end doesn't exist either. Still return nullptr.
   EXPECT_EQ(nullptr,
             GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id2)));
 }
@@ -2310,7 +2308,9 @@
 
   // |child_id1| now should have a temporary reference.
   EXPECT_TRUE(HasTemporaryReference(child_id1));
-  EXPECT_FALSE(HasPersistentReference(child_id1));
+  EXPECT_TRUE(surface_manager()
+                  ->GetSurfacesThatReferenceChildForTesting(child_id1)
+                  .empty());
 
   // Activate |child_id2|.
   child_support1().SubmitCompositorFrame(child_id2.local_surface_id(),
@@ -2318,7 +2318,9 @@
 
   // |child_id2| now should have a temporary reference.
   EXPECT_TRUE(HasTemporaryReference(child_id2));
-  EXPECT_FALSE(HasPersistentReference(child_id2));
+  EXPECT_TRUE(surface_manager()
+                  ->GetSurfacesThatReferenceChildForTesting(child_id2)
+                  .empty());
 
   // Create a reference from |parent_id| to |child_id2|.
   parent_support().SubmitCompositorFrame(
@@ -2328,11 +2330,15 @@
 
   // |child_id1| have no references and can be garbage collected.
   EXPECT_FALSE(HasTemporaryReference(child_id1));
-  EXPECT_FALSE(HasPersistentReference(child_id1));
+  EXPECT_TRUE(surface_manager()
+                  ->GetSurfacesThatReferenceChildForTesting(child_id1)
+                  .empty());
 
   // |child_id2| has a persistent references now.
   EXPECT_FALSE(HasTemporaryReference(child_id2));
-  EXPECT_TRUE(HasPersistentReference(child_id2));
+  EXPECT_FALSE(surface_manager()
+                   ->GetSurfacesThatReferenceChildForTesting(child_id2)
+                   .empty());
 
   // Verify that GetLatestInFlightSurface returns |child_id2|.
   EXPECT_EQ(GetSurfaceForId(child_id2),
diff --git a/components/viz/service/surfaces/surface_allocation_group.cc b/components/viz/service/surfaces/surface_allocation_group.cc
index dc3e9e62..dc42639 100644
--- a/components/viz/service/surfaces/surface_allocation_group.cc
+++ b/components/viz/service/surfaces/surface_allocation_group.cc
@@ -34,4 +34,82 @@
   surfaces_.erase(it);
 }
 
+Surface* SurfaceAllocationGroup::FindLatestActiveSurfaceInRange(
+    const SurfaceRange& range) const {
+  // If the embed token of the end of the SurfaceRange matches that of this
+  // group, find the latest active surface that is older than or equal to the
+  // end, then check that it's not older than start.
+  if (range.end().local_surface_id().embed_token() == embed_token_) {
+    DCHECK_EQ(submitter_, range.end().frame_sink_id());
+    Surface* result = FindOlderOrEqual(range.end());
+    if (result &&
+        (!range.start() || !range.start()->IsNewerThan(result->surface_id()))) {
+      return result;
+    } else {
+      return nullptr;
+    }
+  }
+
+  // If we are here, the embed token of the end of the range doesn't match this
+  // group's embed token. In this case, the range must have a start and its
+  // embed token must match this group. Simply find the last active surface, and
+  // check whether it's newer than the range's start.
+  DCHECK(range.start());
+  DCHECK_EQ(embed_token_, range.start()->local_surface_id().embed_token());
+  DCHECK_NE(embed_token_, range.end().local_surface_id().embed_token());
+  DCHECK_EQ(submitter_, range.start()->frame_sink_id());
+
+  Surface* result = nullptr;
+  // Normally there is at most one pending surface, so this for loop shouldn't
+  // take more than two iterations.
+  for (int i = surfaces_.size() - 1; i >= 0; i--) {
+    if (surfaces_[i]->HasActiveFrame()) {
+      result = surfaces_[i];
+      break;
+    }
+  }
+  if (result && range.start()->IsNewerThan(result->surface_id()))
+    return nullptr;
+  return result;
+}
+
+Surface* SurfaceAllocationGroup::FindOlderOrEqual(
+    const SurfaceId& surface_id) const {
+  DCHECK_EQ(submitter_, surface_id.frame_sink_id());
+  DCHECK_EQ(embed_token_, surface_id.local_surface_id().embed_token());
+
+  // Return early if there are no surfaces in this group.
+  if (surfaces_.empty())
+    return nullptr;
+
+  // If even the first surface is newer than |surface_id|, we can't find a
+  // surface that is older than or equal to |surface_id|.
+  if (surfaces_[0]->surface_id().IsNewerThan(surface_id))
+    return nullptr;
+
+  // Perform a binary search the find the latest active surface that is older
+  // than or equal to |surface_id|.
+  int begin = 0;
+  int end = surfaces_.size();
+  while (end - begin > 1) {
+    int avg = (begin + end) / 2;
+    if (surfaces_[avg]->surface_id().IsNewerThan(surface_id))
+      end = avg;
+    else
+      begin = avg;
+  }
+
+  // We have found the latest surface. Now keep iterating back until we find an
+  // active surface. Normally, there is only one pending surface at a time, so
+  // this shouldn't take more than two iterations.
+  for (; begin >= 0; --begin) {
+    if (surfaces_[begin]->HasActiveFrame())
+      return surfaces_[begin];
+  }
+
+  // No active surface was found, so return null.
+  DCHECK_EQ(-1, begin);
+  return nullptr;
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_allocation_group.h b/components/viz/service/surfaces/surface_allocation_group.h
index 38e99e29..780ce97 100644
--- a/components/viz/service/surfaces/surface_allocation_group.h
+++ b/components/viz/service/surfaces/surface_allocation_group.h
@@ -9,6 +9,7 @@
 
 #include "base/unguessable_token.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/surface_range.h"
 #include "components/viz/service/viz_service_export.h"
 
 namespace viz {
@@ -41,12 +42,21 @@
   // allocation group.
   void UnregisterSurface(Surface* surface);
 
+  // Returns the latest active surface in the given range that is a part of this
+  // allocation group. The embed token of at least one end of the range must
+  // match the embed token of this group.
+  Surface* FindLatestActiveSurfaceInRange(const SurfaceRange& range) const;
+
   // Returns the last surface created in this allocation group.
-  Surface* last_created_surface() {
+  Surface* last_created_surface() const {
     return surfaces_.empty() ? nullptr : surfaces_.back();
   }
 
  private:
+  // Helper method for FindLatestActiveSurfaceInRange. Returns the latest active
+  // surface whose SurfaceId is older than or equal to |surface_id|.
+  Surface* FindOlderOrEqual(const SurfaceId& surface_id) const;
+
   // The ID of the FrameSink that is submitting to the surfaces in this
   // allocation group.
   const FrameSinkId submitter_;
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc
index e9fa75c5..b96a45c8 100644
--- a/components/viz/service/surfaces/surface_manager.cc
+++ b/components/viz/service/surfaces/surface_manager.cc
@@ -65,7 +65,6 @@
   // destroyed.
   temporary_references_.clear();
   temporary_reference_ranges_.clear();
-  persistent_references_by_frame_sink_id_.clear();
   // Create a copy of the children set as RemoveSurfaceReferenceImpl below will
   // mutate that set.
   base::flat_set<SurfaceId> children(
@@ -249,43 +248,6 @@
   return parents;
 }
 
-Surface* SurfaceManager::GetLatestInFlightSurfaceForFrameSinkId(
-    const SurfaceRange& surface_range,
-    const FrameSinkId& sink_id) {
-  std::vector<LocalSurfaceId> valid_local_surfaces;
-  // Get all valid temporary references.
-  auto temporary_it = temporary_reference_ranges_.find(sink_id);
-  if (temporary_it != temporary_reference_ranges_.end()) {
-    for (const LocalSurfaceId& local_id : temporary_it->second) {
-      if (surface_range.IsInRangeInclusive(SurfaceId(sink_id, local_id)))
-        valid_local_surfaces.push_back(local_id);
-    }
-  }
-
-  // Get all valid persistent references.
-  auto persistent_it = persistent_references_by_frame_sink_id_.find(sink_id);
-  if (persistent_it != persistent_references_by_frame_sink_id_.end()) {
-    for (const LocalSurfaceId& local_id : persistent_it->second) {
-      if (surface_range.IsInRangeInclusive(SurfaceId(sink_id, local_id)))
-        valid_local_surfaces.push_back(local_id);
-    }
-  }
-
-  // Sort all possible surfaces from newest to oldest, then return the first
-  // surface that has an active frame.
-  std::sort(valid_local_surfaces.begin(), valid_local_surfaces.end(),
-            [](const LocalSurfaceId& first, const LocalSurfaceId& second) {
-              return first > second;
-            });
-
-  for (const LocalSurfaceId& local_surface_id : valid_local_surfaces) {
-    Surface* surface = GetSurfaceForId(SurfaceId(sink_id, local_surface_id));
-    if (surface && surface->HasActiveFrame())
-      return surface;
-  }
-  return nullptr;
-}
-
 SurfaceManager::SurfaceIdSet SurfaceManager::GetLiveSurfacesForReferences() {
   SurfaceIdSet reachable_surfaces;
 
@@ -343,10 +305,6 @@
 
   references_[parent_id].insert(child_id);
 
-  // Add a real reference to child_id.
-  persistent_references_by_frame_sink_id_[child_id.frame_sink_id()].insert(
-      child_id.local_surface_id());
-
   for (auto& observer : observer_list_)
     observer.OnAddedSurfaceReference(parent_id, child_id);
 
@@ -373,36 +331,12 @@
   iter_parent->second.erase(child_iter);
   if (iter_parent->second.empty())
     references_.erase(iter_parent);
-
-  // Remove the presistent reference.
-  const FrameSinkId& sink_id = child_id.frame_sink_id();
-  const LocalSurfaceId& local_id = child_id.local_surface_id();
-
-  auto sink_it = persistent_references_by_frame_sink_id_.find(sink_id);
-  if (sink_it == persistent_references_by_frame_sink_id_.end())
-    return;
-
-  auto local_surface_it = sink_it->second.find(local_id);
-  if (local_surface_it == sink_it->second.end())
-    return;
-
-  sink_it->second.erase(local_surface_it);
-  if (sink_it->second.empty())
-    persistent_references_by_frame_sink_id_.erase(sink_it);
 }
 
 bool SurfaceManager::HasTemporaryReference(const SurfaceId& surface_id) const {
   return temporary_references_.count(surface_id) != 0;
 }
 
-bool SurfaceManager::HasPersistentReference(const SurfaceId& surface_id) const {
-  auto it =
-      persistent_references_by_frame_sink_id_.find(surface_id.frame_sink_id());
-  if (it == persistent_references_by_frame_sink_id_.end())
-    return false;
-  return it->second.count(surface_id.local_surface_id()) != 0;
-}
-
 void SurfaceManager::AddTemporaryReference(const SurfaceId& surface_id) {
   DCHECK(!HasTemporaryReference(surface_id));
 
@@ -462,29 +396,24 @@
 
 Surface* SurfaceManager::GetLatestInFlightSurface(
     const SurfaceRange& surface_range) {
-  // If primary exists, we return it.
-  Surface* primary_surface = GetSurfaceForId(surface_range.end());
-  if (primary_surface && primary_surface->HasActiveFrame())
-    return primary_surface;
-
-  // If both end of the range exists, we try the primary's FrameSinkId first.
-  Surface* latest_surface = GetLatestInFlightSurfaceForFrameSinkId(
-      surface_range, surface_range.end().frame_sink_id());
-
-  // If the fallback has a different FrameSinkId, then try that also.
-  if (!latest_surface && surface_range.HasDifferentFrameSinkIds()) {
-    latest_surface = GetLatestInFlightSurfaceForFrameSinkId(
-        surface_range, surface_range.start()->frame_sink_id());
+  SurfaceAllocationGroup* end_allocation_group =
+      GetAllocationGroupForSurfaceId(surface_range.end());
+  if (end_allocation_group) {
+    Surface* result =
+        end_allocation_group->FindLatestActiveSurfaceInRange(surface_range);
+    if (result)
+      return result;
   }
-
-  // Fallback might have neither temporary or presistent references, so we
-  // consider it separately.
-  if (!latest_surface && surface_range.start())
-    latest_surface = GetSurfaceForId(*surface_range.start());
-
-  if (latest_surface && latest_surface->HasActiveFrame())
-    return latest_surface;
-  return nullptr;
+  if (!surface_range.start() ||
+      surface_range.start()->local_surface_id().embed_token() ==
+          surface_range.end().local_surface_id().embed_token()) {
+    return nullptr;
+  }
+  SurfaceAllocationGroup* start_allocation_group =
+      GetAllocationGroupForSurfaceId(*surface_range.start());
+  if (!start_allocation_group)
+    return nullptr;
+  return start_allocation_group->FindLatestActiveSurfaceInRange(surface_range);
 }
 
 void SurfaceManager::ExpireOldTemporaryReferences() {
@@ -687,4 +616,18 @@
   return allocation_group.get();
 }
 
+SurfaceAllocationGroup* SurfaceManager::GetAllocationGroupForSurfaceId(
+    const SurfaceId& surface_id) {
+  auto it = embed_token_to_allocation_group_.find(
+      surface_id.local_surface_id().embed_token());
+  if (it == embed_token_to_allocation_group_.end())
+    return nullptr;
+  DCHECK(it->second);
+  if (it->second->submitter_frame_sink_id() != surface_id.frame_sink_id()) {
+    DLOG(ERROR) << "Cannot reuse embed token across frame sinks";
+    return nullptr;
+  }
+  return it->second.get();
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_manager.h b/components/viz/service/surfaces/surface_manager.h
index 39811e121..8221842 100644
--- a/components/viz/service/surfaces/surface_manager.h
+++ b/components/viz/service/surfaces/surface_manager.h
@@ -206,6 +206,11 @@
   SurfaceAllocationGroup* GetOrCreateAllocationGroupForSurfaceId(
       const SurfaceId& surface_id);
 
+  // Similar to GetOrCreateAllocationGroupForSurfaceId, but will not attempt to
+  // create the allocation group if it does not already exist.
+  SurfaceAllocationGroup* GetAllocationGroupForSurfaceId(
+      const SurfaceId& surface_id);
+
  private:
   friend class CompositorFrameSinkSupportTest;
   friend class FrameSinkManagerTest;
@@ -233,11 +238,6 @@
     bool marked_as_old = false;
   };
 
-  // Returns the latest surface in a FrameSinkId that satisfies |is_valid|.
-  Surface* GetLatestInFlightSurfaceForFrameSinkId(
-      const SurfaceRange& surface_range,
-      const FrameSinkId& sink_id);
-
   // Returns set of live surfaces for |lifetime_manager_| is REFERENCES.
   SurfaceIdSet GetLiveSurfacesForReferences();
 
@@ -254,9 +254,6 @@
   // Returns whether |surface_id| has a temporary reference or not.
   bool HasTemporaryReference(const SurfaceId& surface_id) const;
 
-  // Returns whether |surface_id| has a Persistent reference or not.
-  bool HasPersistentReference(const SurfaceId& surface_id) const;
-
   // Adds a temporary reference to |surface_id|. The reference will not have an
   // owner initially.
   void AddTemporaryReference(const SurfaceId& surface_id);
@@ -331,11 +328,6 @@
   std::unordered_map<FrameSinkId, std::vector<LocalSurfaceId>, FrameSinkIdHash>
       temporary_reference_ranges_;
 
-  // A list of surfaces with a given FrameSinkId that have a persistent
-  // reference.
-  base::flat_map<FrameSinkId, base::flat_set<LocalSurfaceId>>
-      persistent_references_by_frame_sink_id_;
-
   // A map storing SurfaceIds interested in knowing about activation events
   // happending in FrameSinkId.
   base::flat_map<FrameSinkId, base::flat_set<SurfaceId>> activation_observers_;
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index f0207f0..4fa2f08 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1927,16 +1927,16 @@
     const protocol::Binary& raw = raw_response.fromJust();
 
     std::string raw_headers;
-    int header_size = net::HttpUtil::LocateEndOfHeaders(
+    size_t header_size = net::HttpUtil::LocateEndOfHeaders(
         reinterpret_cast<const char*>(raw.data()), raw.size());
-    if (header_size == -1) {
+    if (header_size == std::string::npos) {
       LOG(WARNING) << "Can't find headers in raw response";
       header_size = 0;
     } else {
       raw_headers = net::HttpUtil::AssembleRawHeaders(
           reinterpret_cast<const char*>(raw.data()), header_size);
     }
-    CHECK_LE(static_cast<size_t>(header_size), raw.size());
+    CHECK_LE(header_size, raw.size());
     response_headers =
         base::MakeRefCounted<net::HttpResponseHeaders>(std::move(raw_headers));
     response_body = raw.bytes();
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 073cc1cc..d30899a 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -33,7 +33,6 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/common/child_process_host.h"
-#include "content/public/common/content_client.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "net/base/net_errors.h"
@@ -155,7 +154,6 @@
       reload_type_(ReloadType::NONE),
       restore_type_(RestoreType::NONE),
       navigation_type_(NAVIGATION_TYPE_UNKNOWN),
-      expected_render_process_host_id_(ChildProcessHost::kInvalidUniqueID),
       is_same_process_(true),
       throttle_runner_(this, this),
       weak_factory_(this) {
@@ -167,12 +165,6 @@
   DCHECK(!navigation_request_->common_params().navigation_start.is_null());
   DCHECK(!IsRendererDebugURL(url));
 
-  starting_site_instance_ =
-      frame_tree_node()->current_frame_host()->GetSiteInstance();
-
-  site_url_ = SiteInstanceImpl::GetSiteForURL(
-      starting_site_instance_->GetBrowserContext(),
-      starting_site_instance_->GetIsolationContext(), url);
   if (redirect_chain_.empty())
     redirect_chain_.push_back(url);
 
@@ -230,17 +222,6 @@
 }
 
 NavigationHandleImpl::~NavigationHandleImpl() {
-  // Inform the RenderProcessHost to no longer expect a navigation.
-  if (expected_render_process_host_id_ != ChildProcessHost::kInvalidUniqueID) {
-    RenderProcessHost* process =
-        RenderProcessHost::FromID(expected_render_process_host_id_);
-    if (process) {
-      RenderProcessHostImpl::RemoveExpectedNavigationToSite(
-          frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
-          process, site_url_);
-    }
-  }
-
 #if defined(OS_ANDROID)
   navigation_handle_proxy_->DidFinish();
 #endif
@@ -269,7 +250,7 @@
 }
 
 SiteInstanceImpl* NavigationHandleImpl::GetStartingSiteInstance() {
-  return starting_site_instance_.get();
+  return navigation_request_->starting_site_instance();
 }
 
 bool NavigationHandleImpl::IsInMainFrame() {
@@ -636,7 +617,7 @@
                                "WillRedirectRequest", "url",
                                GetURL().possibly_invalid_spec());
   UpdateStateFollowingRedirect(new_referrer_url, std::move(callback));
-  UpdateSiteURL(post_redirect_process);
+  navigation_request_->UpdateSiteURL(post_redirect_process);
 
   if (IsSelfReferentialURL()) {
     state_ = CANCELING;
@@ -731,7 +712,7 @@
     }
   }
 
-  SetExpectedProcess(GetRenderFrameHost()->GetProcess());
+  navigation_request_->SetExpectedProcess(GetRenderFrameHost()->GetProcess());
 
   if (!IsSameDocument())
     GetDelegate()->ReadyToCommitNavigation(this);
@@ -829,38 +810,6 @@
   }
 }
 
-void NavigationHandleImpl::SetExpectedProcess(
-    RenderProcessHost* expected_process) {
-  if (expected_process &&
-      expected_process->GetID() == expected_render_process_host_id_) {
-    // This |expected_process| has already been informed of the navigation,
-    // no need to update it again.
-    return;
-  }
-
-  // If a RenderProcessHost was expecting this navigation to commit, have it
-  // stop tracking this site.
-  RenderProcessHost* old_process =
-      RenderProcessHost::FromID(expected_render_process_host_id_);
-  if (old_process) {
-    RenderProcessHostImpl::RemoveExpectedNavigationToSite(
-        frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
-        old_process, site_url_);
-  }
-
-  if (expected_process == nullptr) {
-    expected_render_process_host_id_ = ChildProcessHost::kInvalidUniqueID;
-    return;
-  }
-
-  // Keep track of the speculative RenderProcessHost and tell it to expect a
-  // navigation to |site_url_|.
-  expected_render_process_host_id_ = expected_process->GetID();
-  RenderProcessHostImpl::AddExpectedNavigationToSite(
-      frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
-      expected_process, site_url_);
-}
-
 void NavigationHandleImpl::OnNavigationEventProcessed(
     NavigationThrottleRunner::Event event,
     NavigationThrottle::ThrottleCheckResult result) {
@@ -1021,30 +970,6 @@
   return false;
 }
 
-void NavigationHandleImpl::UpdateSiteURL(
-    RenderProcessHost* post_redirect_process) {
-  // TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
-  // be correct for cross-BrowsingInstance redirects.
-  GURL new_site_url = SiteInstanceImpl::GetSiteForURL(
-      frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
-      starting_site_instance_->GetIsolationContext(), GetURL());
-  int post_redirect_process_id = post_redirect_process
-                                     ? post_redirect_process->GetID()
-                                     : ChildProcessHost::kInvalidUniqueID;
-  if (new_site_url == site_url_ &&
-      post_redirect_process_id == expected_render_process_host_id_) {
-    return;
-  }
-
-  // Stop expecting a navigation to the current site URL in the current expected
-  // process.
-  SetExpectedProcess(nullptr);
-
-  // Update the site URL and the expected process.
-  site_url_ = new_site_url;
-  SetExpectedProcess(post_redirect_process);
-}
-
 void NavigationHandleImpl::RenderProcessBlockedStateChanged(bool blocked) {
   if (blocked)
     StopCommitTimeout();
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index 7b636cbd..17b4489f 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -326,11 +326,6 @@
     return std::move(modified_request_headers_);
   }
 
-  // Sets ID of the RenderProcessHost we expect the navigation to commit in.
-  // This is used to inform the RenderProcessHost to expect a navigation to the
-  // url we're navigating to.
-  void SetExpectedProcess(RenderProcessHost* expected_process);
-
   NavigationThrottle* GetDeferringThrottleForTesting() const {
     return throttle_runner_.GetDeferringThrottle();
   }
@@ -392,12 +387,6 @@
   // WillStartRequest and WillRedirectRequest to prevent the navigation.
   bool IsSelfReferentialURL();
 
-  // Updates the destination site URL for this navigation. This is called on
-  // redirects. |post_redirect_process| is the renderer process that should
-  // handle the navigation following the redirect if it can be handled by an
-  // existing RenderProcessHost. Otherwise, it should be null.
-  void UpdateSiteURL(RenderProcessHost* post_redirect_process);
-
   // Called if READY_TO_COMMIT -> COMMIT state transition takes an unusually
   // long time.
   void OnCommitTimeout();
@@ -413,7 +402,6 @@
   NavigationRequest* navigation_request_;
 
   // See NavigationHandle for a description of those member variables.
-  scoped_refptr<SiteInstanceImpl> starting_site_instance_;
   Referrer sanitized_referrer_;
   net::Error net_error_code_;
   const bool is_same_document_;
@@ -422,9 +410,6 @@
   bool should_update_history_;
   bool subframe_entry_committed_;
 
-  // The site URL of this navigation, as obtained from SiteInstance::GetSiteURL.
-  GURL site_url_;
-
   // The headers used for the request.
   net::HttpRequestHeaders request_headers_;
 
@@ -499,10 +484,6 @@
   GURL base_url_;
   NavigationType navigation_type_;
 
-  // Used to inform a RenderProcessHost that we expect this navigation to commit
-  // in it.
-  int expected_render_process_host_id_;
-
   // Which proxy server was used for this navigation, if any.
   net::ProxyServer proxy_server_;
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 81c4941..68b3df2 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -496,6 +496,7 @@
       from_begin_navigation_(from_begin_navigation),
       has_stale_copy_in_cache_(false),
       net_error_(net::OK),
+      expected_render_process_host_id_(ChildProcessHost::kInvalidUniqueID),
       devtools_navigation_token_(base::UnguessableToken::Create()),
       request_navigation_client_(nullptr),
       commit_navigation_client_(nullptr),
@@ -542,6 +543,18 @@
     bindings_ = entry->bindings();
   }
 
+  // This is needed to get site URLs and assign the expected RenderProcessHost.
+  // This is not always the same as |source_site_instance|, as it only depends
+  // on the current frame host, and does not depend on |entry|.
+  starting_site_instance_ =
+      frame_tree_node->current_frame_host()->GetSiteInstance();
+
+  // TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
+  // be correct for cross-BrowsingInstance redirects.
+  site_url_ = SiteInstanceImpl::GetSiteForURL(
+      starting_site_instance_->GetBrowserContext(),
+      starting_site_instance_->GetIsolationContext(), common_params_.url);
+
   // Update the load flags with cache information.
   UpdateLoadFlagsWithCacheFlags(&begin_params_->load_flags,
                                 common_params_.navigation_type,
@@ -617,6 +630,7 @@
 
 NavigationRequest::~NavigationRequest() {
   TRACE_EVENT_ASYNC_END0("navigation", "NavigationRequest", this);
+  ResetExpectedProcess();
   if (state_ == STARTED) {
     devtools_instrumentation::OnNavigationRequestFailed(
         *this, network::URLLoaderCompletionStatus(net::ERR_ABORTED));
@@ -1270,6 +1284,7 @@
 
 void NavigationRequest::OnRequestFailed(
     const network::URLLoaderCompletionStatus& status) {
+  DCHECK_NE(status.error_code, net::OK);
   bool collapse_frame =
       status.extended_error_code ==
       static_cast<int>(blink::ResourceRequestBlockedReason::kCollapsedByClient);
@@ -1332,7 +1347,7 @@
     // TODO(nasko): Investigate whether GetFrameHostForNavigation can properly
     // account for clearing the expected process if it clears the speculative
     // RenderFrameHost. See https://crbug.com/793127.
-    navigation_handle_->SetExpectedProcess(nullptr);
+    ResetExpectedProcess();
     render_frame_host =
         frame_tree_node_->render_manager()->GetFrameHostForNavigation(*this);
   } else {
@@ -1448,7 +1463,7 @@
           : frame_tree_node_->current_frame_host();
   DCHECK(navigating_frame_host);
 
-  navigation_handle_->SetExpectedProcess(navigating_frame_host->GetProcess());
+  SetExpectedProcess(navigating_frame_host->GetProcess());
 
   BrowserContext* browser_context =
       frame_tree_node_->navigator()->GetController()->GetBrowserContext();
@@ -1859,6 +1874,69 @@
       render_frame_host_->GetSiteInstance()->GetBrowserContext());
 }
 
+void NavigationRequest::ResetExpectedProcess() {
+  if (expected_render_process_host_id_ == ChildProcessHost::kInvalidUniqueID) {
+    // No expected process is set, nothing to update.
+    return;
+  }
+  RenderProcessHost* process =
+      RenderProcessHost::FromID(expected_render_process_host_id_);
+  if (process) {
+    RenderProcessHostImpl::RemoveExpectedNavigationToSite(
+        frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
+        process, site_url_);
+  }
+  expected_render_process_host_id_ = ChildProcessHost::kInvalidUniqueID;
+}
+
+void NavigationRequest::SetExpectedProcess(
+    RenderProcessHost* expected_process) {
+  if (expected_process &&
+      expected_process->GetID() == expected_render_process_host_id_) {
+    // This |expected_process| has already been informed of the navigation,
+    // no need to update it again.
+    return;
+  }
+
+  ResetExpectedProcess();
+
+  if (expected_process == nullptr) {
+    expected_render_process_host_id_ = ChildProcessHost::kInvalidUniqueID;
+    return;
+  }
+
+  // Keep track of the speculative RenderProcessHost and tell it to expect a
+  // navigation to |site_url_|.
+  expected_render_process_host_id_ = expected_process->GetID();
+  RenderProcessHostImpl::AddExpectedNavigationToSite(
+      frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
+      expected_process, site_url_);
+}
+
+void NavigationRequest::UpdateSiteURL(
+    RenderProcessHost* post_redirect_process) {
+  // TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
+  // be correct for cross-BrowsingInstance redirects.
+  GURL new_site_url = SiteInstanceImpl::GetSiteForURL(
+      frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
+      starting_site_instance_->GetIsolationContext(), common_params_.url);
+  int post_redirect_process_id = post_redirect_process
+                                     ? post_redirect_process->GetID()
+                                     : ChildProcessHost::kInvalidUniqueID;
+  if (new_site_url == site_url_ &&
+      post_redirect_process_id == expected_render_process_host_id_) {
+    return;
+  }
+
+  // Stop expecting a navigation to the current site URL in the current expected
+  // process.
+  ResetExpectedProcess();
+
+  // Update the site URL and the expected process.
+  site_url_ = new_site_url;
+  SetExpectedProcess(post_redirect_process);
+}
+
 bool NavigationRequest::IsAllowedByCSPDirective(
     CSPContext* context,
     CSPDirective::Name directive,
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index a8ec710d..7c16f465 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -164,6 +164,10 @@
 
   int bindings() const { return bindings_; }
 
+  SiteInstanceImpl* starting_site_instance() const {
+    return starting_site_instance_.get();
+  }
+
   bool browser_initiated() const { return browser_initiated_ ; }
 
   bool from_begin_navigation() const { return from_begin_navigation_; }
@@ -213,6 +217,17 @@
     on_start_checks_complete_closure_ = closure;
   }
 
+  // Sets ID of the RenderProcessHost we expect the navigation to commit in.
+  // This is used to inform the RenderProcessHost to expect a navigation to the
+  // url we're navigating to.
+  void SetExpectedProcess(RenderProcessHost* expected_process);
+
+  // Updates the destination site URL for this navigation. This is called on
+  // redirects. |post_redirect_process| is the renderer process that should
+  // handle the navigation following the redirect if it can be handled by an
+  // existing RenderProcessHost. Otherwise, it should be null.
+  void UpdateSiteURL(RenderProcessHost* post_redirect_process);
+
   int nav_entry_id() const { return nav_entry_id_; }
 
   // For automation driver-initiated navigations over the devtools protocol,
@@ -396,6 +411,9 @@
   // Only used with PerNavigationMojoInterface enabled.
   void IgnoreInterfaceDisconnection();
 
+  // Inform the RenderProcessHost to no longer expect a navigation.
+  void ResetExpectedProcess();
+
   FrameTreeNode* frame_tree_node_;
 
   RenderFrameHostImpl* render_frame_host_ = nullptr;
@@ -442,6 +460,8 @@
   int bindings_;
   int nav_entry_id_ = 0;
 
+  scoped_refptr<SiteInstanceImpl> starting_site_instance_;
+
   // Whether the navigation should be sent to a renderer a process. This is
   // true, except for 204/205 responses and downloads.
   bool response_should_be_rendered_;
@@ -476,6 +496,13 @@
   bool has_stale_copy_in_cache_;
   int net_error_;
 
+  // Identifies in which RenderProcessHost this navigation is expected to
+  // commit.
+  int expected_render_process_host_id_;
+
+  // The site URL of this navigation, as obtained from SiteInstance::GetSiteURL.
+  GURL site_url_;
+
   std::unique_ptr<InitiatorCSPContext> initiator_csp_context_;
 
   base::Closure on_start_checks_complete_closure_;
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index db40189..4f1a7d82 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
@@ -1315,21 +1316,28 @@
     UMA_HISTOGRAM_BOOLEAN(
         "Navigation.URLLoaderNetworkService.OnCompleteHasSSLInfo",
         status.ssl_info.has_value());
+
+    // Successful load must have used OnResponseStarted first. In this case, the
+    // URLLoaderClient has already been transferred to the renderer process and
+    // OnComplete is not expected to be called here.
+    if (status.error_code == net::OK) {
+      base::debug::DumpWithoutCrashing();
+      return;
+    }
+
     if (status.ssl_info.has_value()) {
       UMA_HISTOGRAM_MEMORY_KB(
           "Navigation.URLLoaderNetworkService.OnCompleteCertificateChainsSize",
           GetCertificateChainsSizeInKB(status.ssl_info.value()));
     }
 
-    if (status.error_code != net::OK && !received_response_) {
-      // If the default loader (network) was used to handle the URL load
-      // request we need to see if the interceptors want to potentially create a
-      // new loader for the response. e.g. AppCache.
-      if (MaybeCreateLoaderForResponse(network::ResourceResponseHead()))
-        return;
-    }
-    status_ = status;
+    // If the default loader (network) was used to handle the URL load
+    // request we need to see if the interceptors want to potentially create a
+    // new loader for the response. e.g. AppCache.
+    if (MaybeCreateLoaderForResponse(network::ResourceResponseHead()))
+      return;
 
+    status_ = status;
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::UI},
         base::BindOnce(&NavigationURLLoaderImpl::OnComplete, owner_, status));
@@ -1793,12 +1801,8 @@
 
 void NavigationURLLoaderImpl::OnComplete(
     const network::URLLoaderCompletionStatus& status) {
-  if (status.error_code == net::OK)
-    return;
-
   TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this,
                          "&NavigationURLLoaderImpl", this, "success", false);
-
   delegate_->OnRequestFailed(status);
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 4e8b510c..79e3fb5d 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -331,6 +331,10 @@
   return touch_event_ack_queue_->length_for_testing();
 }
 
+size_t RenderWidgetHostInputEventRouter::RegisteredViewCountForTesting() const {
+  return owner_map_.size();
+}
+
 void RenderWidgetHostInputEventRouter::OnRenderWidgetHostViewBaseDestroyed(
     RenderWidgetHostViewBase* view) {
   // RenderWidgetHostViewBase::RemoveObserver() should only ever be called
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index ca8cd0c..137dd83 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -169,6 +169,7 @@
   bool HasEventsPendingDispatch() const;
 
   size_t TouchEventAckQueueLengthForTesting() const;
+  size_t RegisteredViewCountForTesting() const;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(BrowserSideFlingBrowserTest,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 770cdea..26d4e7aa 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -14653,4 +14653,5 @@
              InnerWebContentsAttachChildFrameOriginType::kCrossOrigin}),
         testing::Bool(),
         testing::Bool()));
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 28984b3..58c6678 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1894,6 +1894,20 @@
   return web_contents;
 }
 
+void WebContentsImpl::RecursivelyRegisterFrameSinkIds() {
+  auto* view = static_cast<RenderWidgetHostViewBase*>(
+      GetRenderManager()->GetRenderWidgetHostView());
+  DCHECK(view);
+  if (!view->IsRenderWidgetHostViewChildFrame())
+    return;
+  static_cast<RenderWidgetHostViewChildFrame*>(view)->RegisterFrameSinkId();
+
+  for (auto* inner_web_contents : node_.GetInnerWebContents()) {
+    static_cast<WebContentsImpl*>(inner_web_contents)
+        ->RecursivelyRegisterFrameSinkIds();
+  }
+}
+
 void WebContentsImpl::ReattachToOuterWebContentsFrame() {
   DCHECK(node_.outer_web_contents());
   auto* render_manager = GetRenderManager();
@@ -1902,9 +1916,7 @@
   render_manager->SetRWHViewForInnerContents(
       render_manager->GetRenderWidgetHostView());
 
-  static_cast<RenderWidgetHostViewChildFrame*>(
-      render_manager->GetRenderWidgetHostView())
-      ->RegisterFrameSinkId();
+  RecursivelyRegisterFrameSinkIds();
 
   // Set up the the guest's AX tree to point back at the embedder's AX tree.
   GetMainFrame()->set_browser_plugin_embedder_ax_tree_id(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 568aa2e..25fcc96d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1301,6 +1301,10 @@
   // These functions are helpers in managing a hierarchy of WebContents
   // involved in rendering inner WebContents.
 
+  // Registers FrameSinkIds for all WebContents in the subtree of
+  // WebContentsTree, rooted at |contents|.
+  void RecursivelyRegisterFrameSinkIds();
+
   // When multiple WebContents are present within a tab or window, a single one
   // is focused and will route keyboard events in most cases to a RenderWidget
   // contained within it. |GetFocusedWebContents()|'s main frame widget will
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index be9986ee..d0e1df7 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/common/frame_messages.h"
@@ -3265,4 +3266,50 @@
   EXPECT_TRUE(EvalJs(web_contents.get(), "document.hidden").ExtractBool());
 }
 
+// This test verifies that if we attach an inner WebContents that has
+// descendants in the WebContentsTree, that the descendants also have their
+// views registered with the top-level WebContents' InputEventRouter. This
+// ensures the descendants will receive events that should be routed to them.
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+                       AttachNestedInnerWebContents) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  auto* root_web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  FrameTreeNode* root = root_web_contents->GetFrameTree()->root();
+  ASSERT_EQ(1u, root->child_count());
+  FrameTreeNode* child_to_replace = root->child_at(0);
+  auto* child_to_replace_rfh = child_to_replace->current_frame_host();
+
+  WebContents::CreateParams inner_params(
+      root_web_contents->GetBrowserContext());
+
+  std::unique_ptr<WebContents> child_contents_ptr =
+      WebContents::Create(inner_params);
+  auto* child_rfh =
+      static_cast<RenderFrameHostImpl*>(child_contents_ptr->GetMainFrame());
+
+  std::unique_ptr<WebContents> grandchild_contents_ptr =
+      WebContents::Create(inner_params);
+
+  // Attach grandchild to child.
+  child_contents_ptr->AttachInnerWebContents(std::move(grandchild_contents_ptr),
+                                             child_rfh);
+
+  // At this point the child hasn't been attached to the root.
+  EXPECT_EQ(1U, root_web_contents->GetInputEventRouter()
+                    ->RegisteredViewCountForTesting());
+
+  // Attach child+grandchild subtree to root.
+  root_web_contents->AttachInnerWebContents(std::move(child_contents_ptr),
+                                            child_to_replace_rfh);
+
+  // Verify views registered for both child and grandchild.
+  EXPECT_EQ(3U, root_web_contents->GetInputEventRouter()
+                    ->RegisteredViewCountForTesting());
+}
+
 }  // namespace content
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index f6d57ef..b280737 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -29,7 +29,6 @@
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
@@ -178,19 +177,17 @@
 #if DCHECK_IS_ON()
   // Verify the combination of the given args based on the flags. See the
   // function comment for details.
+  DCHECK(service_worker_provider_info);
+  DCHECK(subresource_loader_factories);
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     // NetworkService (PlzWorker):
-    DCHECK(service_worker_provider_info);
     DCHECK(!main_script_loader_factory);
     DCHECK(main_script_load_params);
-    DCHECK(subresource_loader_factories);
     DCHECK(!subresource_loader_factories->default_factory_info());
   } else {
     // non-NetworkService:
-    DCHECK(service_worker_provider_info);
     DCHECK(main_script_loader_factory);
     DCHECK(!main_script_load_params);
-    DCHECK(subresource_loader_factories);
     DCHECK(subresource_loader_factories->default_factory_info());
     DCHECK(!controller);
     DCHECK(!controller_service_worker_object_host);
@@ -237,7 +234,7 @@
       mojo::MakeRequest(&interface_provider)));
 
   // Set the default factory to the bundle for subresource loading to pass to
-  // the renderer when NetworkService is on. When S13nServiceWorker is on, the
+  // the renderer when NetworkService is on. When NetworkService is off, the
   // default factory is already provided by
   // WorkerScriptFetchInitiator::CreateFactoryBundle().
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 473de50..ef31b0c 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -70,16 +70,13 @@
 
   // Starts the SharedWorker in the renderer process.
   //
-  // S13nServiceWorker:
   // |service_worker_provider_info| is sent to the renderer process and contains
   // information about its ServiceWorkerProviderHost, the browser-side host for
   // supporting the shared worker as a service worker client.
   //
-  // S13nServiceWorker (non-NetworkService):
   // |main_script_loader_factory| is sent to the renderer process and is to be
-  // used to request the shared worker's main script. Currently it's only
-  // non-null when S13nServiceWorker is enabled but NetworkService is disabled,
-  // to allow service worker machinery to observe the request.
+  // used to request the shared worker's main script. This is null when
+  // NetworkService is enabled in favor of |main_script_load_params|.
   //
   // NetworkService (PlzWorker):
   // |main_script_load_params| is sent to the renderer process and to be used to
@@ -97,8 +94,8 @@
   // a ServiceWorker object about the controller is prepared, it is registered
   // to |controller_service_worker_object_host|. These can be non-null only when
   // NetworkService is enabled.
-  // When S13nServiceWorker is enabled but NetworkService is disabled, the
-  // service worker controller is sent via ServiceWorkerContainer#SetController.
+  // When NetworkService is disabled, the service worker controller is sent via
+  // ServiceWorkerContainer#SetController.
   void Start(
       blink::mojom::SharedWorkerFactoryPtr factory,
       blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index f369fa6f..92e2738 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -29,7 +29,6 @@
 #include "services/network/public/cpp/features.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "url/origin.h"
 
 using blink::MessagePortChannel;
@@ -81,8 +80,8 @@
         subresource_loader_factories;
     base::Optional<SubresourceLoaderParams> subresource_loader_params;
 
-    // Set up various mocks based on NetworkService/S13nServiceWorker
-    // configuration. See the comment on SharedWorkerHost::Start() for details.
+    // Set up various mocks based on NetworkService configuration. See the
+    // comment on SharedWorkerHost::Start() for details.
     if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
       provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
       ServiceWorkerProviderHost::PreCreateForSharedWorker(
@@ -101,7 +100,7 @@
 
       subresource_loader_params->appcache_loader_factory_info =
           loader_factory_ptr.PassInterface();
-    } else if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
+    } else {
       provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
       ServiceWorkerProviderHost::PreCreateForSharedWorker(
           helper_->context()->AsWeakPtr(), mock_render_process_host_.GetID(),
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 7ccb93b3..fab8ac8 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -37,7 +37,6 @@
 #include "services/network/loader_util.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_client.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_info.mojom.h"
@@ -195,39 +194,25 @@
   auto weak_host = host->AsWeakPtr();
   worker_hosts_.insert(std::move(host));
 
-  if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    // NetworkService (PlzWorker):
-    // An appcache interceptor is available only when the network service is
-    // enabled.
-    AppCacheNavigationHandleCore* appcache_handle_core = nullptr;
-    if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-      auto appcache_handle = std::make_unique<AppCacheNavigationHandle>(
-          appcache_service_.get(), process_id);
-      appcache_handle_core = appcache_handle->core();
-      weak_host->SetAppCacheHandle(std::move(appcache_handle));
-    }
-
-    WorkerScriptFetchInitiator::Start(
-        process_id, weak_host->instance()->url(),
-        weak_host->instance()->constructor_origin(),
-        RESOURCE_TYPE_SHARED_WORKER, service_worker_context_,
-        appcache_handle_core, std::move(blob_url_loader_factory),
-        storage_partition_,
-        base::BindOnce(&SharedWorkerServiceImpl::DidCreateScriptLoader,
-                       weak_factory_.GetWeakPtr(), std::move(instance),
-                       weak_host, std::move(client), process_id, frame_id,
-                       message_port));
-    return;
+  // NetworkService (PlzWorker):
+  // An appcache interceptor is available only when the network service is
+  // enabled.
+  AppCacheNavigationHandleCore* appcache_handle_core = nullptr;
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    auto appcache_handle = std::make_unique<AppCacheNavigationHandle>(
+        appcache_service_.get(), process_id);
+    appcache_handle_core = appcache_handle->core();
+    weak_host->SetAppCacheHandle(std::move(appcache_handle));
   }
 
-  // Legacy case (to be deprecated):
-  StartWorker(std::move(instance), weak_host, std::move(client), process_id,
-              frame_id, message_port,
-              nullptr /* service_worker_provider_info */,
-              {} /* main_script_loader_factory */,
-              nullptr /* subresource_loader_factories */,
-              nullptr /* main_script_load_params */, nullptr /* controller */,
-              nullptr /* controller_service_worker_object_host */);
+  WorkerScriptFetchInitiator::Start(
+      process_id, weak_host->instance()->url(),
+      weak_host->instance()->constructor_origin(), RESOURCE_TYPE_SHARED_WORKER,
+      service_worker_context_, appcache_handle_core,
+      std::move(blob_url_loader_factory), storage_partition_,
+      base::BindOnce(&SharedWorkerServiceImpl::DidCreateScriptLoader,
+                     weak_factory_.GetWeakPtr(), std::move(instance), weak_host,
+                     std::move(client), process_id, frame_id, message_port));
 }
 
 void SharedWorkerServiceImpl::DidCreateScriptLoader(
@@ -249,7 +234,6 @@
         controller_service_worker_object_host,
     bool success) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
 
   // NetworkService (PlzWorker):
   // If the script fetcher fails to load shared worker's main script, notify the
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index 7213bb5e..81453c2 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -40,7 +40,6 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/blink/public/common/loader//url_loader_factory_bundle.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "url/origin.h"
@@ -58,7 +57,6 @@
     StoragePartitionImpl* storage_partition,
     CompletionCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK(storage_partition);
   DCHECK(resource_type == RESOURCE_TYPE_WORKER ||
          resource_type == RESOURCE_TYPE_SHARED_WORKER)
@@ -129,7 +127,6 @@
     StoragePartitionImpl* storage_partition,
     bool file_support) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
 
   ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
   GetContentClient()
@@ -254,7 +251,6 @@
         blob_url_loader_factory_info,
     CompletionCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK(resource_context);
 
   // Set up for service worker.
diff --git a/content/browser/worker_host/worker_script_loader_factory.cc b/content/browser/worker_host/worker_script_loader_factory.cc
index 65a7f7d..433a6a67 100644
--- a/content/browser/worker_host/worker_script_loader_factory.cc
+++ b/content/browser/worker_host/worker_script_loader_factory.cc
@@ -14,7 +14,6 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/resource_response.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
 
 namespace content {
@@ -32,7 +31,6 @@
       loader_factory_(std::move(loader_factory)) {
 #if DCHECK_IS_ON()
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   // In the current implementation, dedicated workers use
   // ServiceWorkerProviderHost w/ kForSharedWorker.
   // TODO(nhiroki): Rename it to kForWorker for both dedicated workers and
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 0d817a9..58645639 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -310,7 +310,7 @@
   IPC_STRUCT_TRAITS_MEMBER(connection_info)
   IPC_STRUCT_TRAITS_MEMBER(timing)
   IPC_STRUCT_TRAITS_MEMBER(last_redirect_end_time)
-  IPC_STRUCT_TRAITS_MEMBER(finish_time)
+  IPC_STRUCT_TRAITS_MEMBER(response_end)
   IPC_STRUCT_TRAITS_MEMBER(transfer_size)
   IPC_STRUCT_TRAITS_MEMBER(encoded_body_size)
   IPC_STRUCT_TRAITS_MEMBER(decoded_body_size)
diff --git a/content/common/resource_timing_info.h b/content/common/resource_timing_info.h
index d8301d3..0f3d587 100644
--- a/content/common/resource_timing_info.h
+++ b/content/common/resource_timing_info.h
@@ -72,7 +72,7 @@
   std::string connection_info;
   base::Optional<ResourceLoadTiming> timing;
   base::TimeTicks last_redirect_end_time;
-  base::TimeTicks finish_time;
+  base::TimeTicks response_end;
   uint64_t transfer_size = 0;
   uint64_t encoded_body_size = 0;
   uint64_t decoded_body_size = 0;
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 e457409..957256e 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
@@ -16,6 +16,8 @@
 
 import java.util.WeakHashMap;
 
+import javax.annotation.concurrent.GuardedBy;
+
 /**
  * This {@link TaskExecutor} is for tasks posted with {@link UiThreadTaskTraits}. It maps directly
  * to content::BrowserTaskExecutor except only UI thread posting is supported from java.
@@ -58,6 +60,11 @@
         createSingleThreadTaskRunner(taskTraits).postDelayedTask(task, delay);
     }
 
+    @Override
+    public boolean canRunTaskImmediately(TaskTraits traits) {
+        return createSingleThreadTaskRunner(traits).belongsToCurrentThread();
+    }
+
     public static void register() {
         // In some tests we will get called multiple times.
         if (sRegistered) return;
@@ -67,6 +74,7 @@
                 UiThreadTaskTraitsImpl.DESCRIPTOR.getId(), new BrowserTaskExecutor());
     }
 
+    @GuardedBy("mTaskRunners")
     private final WeakHashMap<TaskTraits, SingleThreadTaskRunner> mTaskRunners =
             new WeakHashMap<>();
 
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 d4081fa..38641d1a 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
@@ -61,13 +61,10 @@
                 }
             }
         });
+
         synchronized (lock) {
-            try {
-                while (!taskExecuted.get()) {
-                    lock.wait();
-                }
-            } catch (InterruptedException ie) {
-                ie.printStackTrace();
+            while (!taskExecuted.get()) {
+                lock.wait();
             }
         }
     }
@@ -88,16 +85,12 @@
         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();
-                }
+        // 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();
         }
     }
 
@@ -169,9 +162,9 @@
     public void testCreateTaskRunnerMigrationToNative() throws Exception {
         final Object lock = new Object();
         final AtomicBoolean taskExecuted = new AtomicBoolean();
-        TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits());
+        TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits());
 
-        taskQueue.postDelayedTask(new Runnable() {
+        postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() {
             @Override
             public void run() {
                 synchronized (lock) {
@@ -179,23 +172,18 @@
                     lock.notify();
                 }
             }
-        }, 1);
-        taskQueue.destroy();
-
-        // 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
+            // The task should run at some point after the migration, so the test shouldn't
             // time out.
             synchronized (lock) {
                 while (!taskExecuted.get()) {
                     lock.wait();
                 }
             }
-        } catch (InterruptedException ie) {
-            ie.printStackTrace();
+        } finally {
+            taskQueue.destroy();
         }
     }
 
@@ -258,12 +246,8 @@
         nativeSchedulerStarted.set(true);
 
         synchronized (lock) {
-            try {
-                while (!taskRun.get()) {
-                    lock.wait();
-                }
-            } catch (InterruptedException ie) {
-                ie.printStackTrace();
+            while (!taskRun.get()) {
+                lock.wait();
             }
         }
     }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
index 67f7bb6..95f4b28 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
@@ -140,6 +140,35 @@
         }
     }
 
+    @Test
+    @MediumTest
+    public void testRunOrPostTask() throws InterruptedException {
+        final Object lock = new Object();
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        List<Integer> orderList = new ArrayList<>();
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+            // We are running on the UI thread now. First, we post a task on the
+            // UI thread; it will not run immediately because the UI thread is
+            // busy running the current code:
+            PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+                orderList.add(1);
+                synchronized (lock) {
+                    taskExecuted.set(true);
+                    lock.notify();
+                }
+            });
+            // Now, we runOrPost a task on the UI thread. We are on the UI thread,
+            // so it will run immediately.
+            PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> { orderList.add(2); });
+        });
+        synchronized (lock) {
+            while (!taskExecuted.get()) {
+                lock.wait();
+            }
+        }
+        assertThat(orderList, contains(2, 1));
+    }
+
     private void startContentMainOnUiThread() {
         final Object lock = new Object();
         final AtomicBoolean uiThreadInitalized = new AtomicBoolean();
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc
index 151f062..a9dfae8 100644
--- a/content/public/test/test_utils.cc
+++ b/content/public/test/test_utils.cc
@@ -244,40 +244,12 @@
       kFakeTrialName, kFakeTrialGroupName, param_values, command_line);
 }
 
-namespace {
-
-// Helper class for CreateAndAttachInnerContents.
-//
-// TODO(lfg): https://crbug.com/821187 Inner webcontentses currently require
-// supplying a BrowserPluginGuestDelegate; however, the oopif architecture
-// doesn't really require it. Refactor this so that we can create an inner
-// contents without any of the guest machinery.
-class InnerWebContentsHelper : public WebContentsObserver {
- public:
-  explicit InnerWebContentsHelper() : WebContentsObserver() {}
-  ~InnerWebContentsHelper() override = default;
-
-  // WebContentsObserver:
-  void WebContentsDestroyed() override { delete this; }
-
-  void SetInnerWebContents(WebContents* inner_web_contents) {
-    Observe(inner_web_contents);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(InnerWebContentsHelper);
-};
-
-}  // namespace
-
 WebContents* CreateAndAttachInnerContents(RenderFrameHost* rfh) {
   WebContents* outer_contents =
       static_cast<RenderFrameHostImpl*>(rfh)->delegate()->GetAsWebContents();
   if (!outer_contents)
     return nullptr;
 
-  auto guest_delegate = std::make_unique<InnerWebContentsHelper>();
-
   WebContents::CreateParams inner_params(outer_contents->GetBrowserContext());
 
   std::unique_ptr<WebContents> inner_contents_ptr =
@@ -287,9 +259,6 @@
   WebContents* inner_contents = inner_contents_ptr.get();
   outer_contents->AttachInnerWebContents(std::move(inner_contents_ptr), rfh);
 
-  // |guest_delegate| becomes owned by |inner_contents|.
-  guest_delegate.release()->SetInnerWebContents(inner_contents);
-
   return inner_contents;
 }
 
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 947d5c6..ff4f8f65 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -857,16 +857,16 @@
       handler_->OnRenegotiationNeeded();
     }
   }
-  void OnStandardizedIceConnectionChange(
-      PeerConnectionInterface::IceConnectionState new_state) override {}
+
   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::OnIceConnectionChange, this,
-              new_state));
+          FROM_HERE, base::BindOnce(&RTCPeerConnectionHandler::Observer::
+                                        OnStandardizedIceConnectionChange,
+                                    this, new_state));
     } else if (handler_) {
       handler_->OnIceConnectionChange(new_state);
     }
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 2bce931..07d65e5 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -1046,7 +1046,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionNew));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionChecking;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1056,7 +1056,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionChecking));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionConnected;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1066,7 +1066,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionConnected));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionCompleted;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1076,7 +1076,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionCompleted));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionFailed;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1086,7 +1086,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionFailed));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionDisconnected;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1096,7 +1096,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionDisconnected));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 
   new_state = webrtc::PeerConnectionInterface::kIceConnectionClosed;
   EXPECT_CALL(*mock_tracker_.get(),
@@ -1106,7 +1106,7 @@
   EXPECT_CALL(*mock_client_.get(),
               DidChangeIceConnectionState(
                   webrtc::PeerConnectionInterface::kIceConnectionClosed));
-  pc_handler_->observer()->OnIceConnectionChange(new_state);
+  pc_handler_->observer()->OnStandardizedIceConnectionChange(new_state);
 }
 
 TEST_F(RTCPeerConnectionHandlerTest, OnIceGatheringChange) {
diff --git a/content/renderer/resource_timing_info_conversions.cc b/content/renderer/resource_timing_info_conversions.cc
index 9a175a8..fa5c48d 100644
--- a/content/renderer/resource_timing_info_conversions.cc
+++ b/content/renderer/resource_timing_info_conversions.cc
@@ -49,7 +49,7 @@
   }
 
   resource_timing.last_redirect_end_time = info.last_redirect_end_time;
-  resource_timing.finish_time = info.finish_time;
+  resource_timing.response_end = info.response_end;
 
   resource_timing.transfer_size = info.transfer_size;
   resource_timing.encoded_body_size = info.encoded_body_size;
@@ -109,7 +109,7 @@
   }
 
   info.last_redirect_end_time = resource_timing.last_redirect_end_time;
-  info.finish_time = resource_timing.finish_time;
+  info.response_end = resource_timing.response_end;
 
   info.transfer_size = resource_timing.transfer_size;
   info.encoded_body_size = resource_timing.encoded_body_size;
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
index cf5707e..18a200b 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -20,7 +20,6 @@
 #include "content/renderer/loader/navigation_response_override_parameters.h"
 #include "content/renderer/loader/tracked_child_url_loader_factory_bundle.h"
 #include "content/renderer/loader/web_worker_fetch_context_impl.h"
-#include "content/renderer/render_thread_impl.h"
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "content/renderer/service_worker/service_worker_provider_context.h"
 #include "content/renderer/worker/service_worker_network_provider_for_worker.h"
@@ -31,7 +30,6 @@
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/privacy_preferences.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
@@ -113,6 +111,7 @@
       renderer_preferences_(renderer_preferences),
       preference_watcher_request_(std::move(preference_watcher_request)),
       appcache_host_id_(appcache_host_id) {
+  DCHECK(factory_bundle);
   // The ID of the precreated AppCacheHost can be valid only when the
   // NetworkService is enabled.
   DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService) ||
@@ -141,45 +140,25 @@
   main_script_loader_factory_ = std::move(main_script_loader_factory);
   controller_info_ = std::move(controller_info);
 
-  // |factory_bundle| is provided in the
-  // ServiceWorkerServicification or NetworkService case.
-  DCHECK(factory_bundle ||
-         !blink::ServiceWorkerUtils::IsServicificationEnabled());
+  // If the network service crashes, then self-destruct so clients don't get
+  // stuck with a worker with a broken loader. Self-destruction is effectively
+  // the same as the worker's process crashing.
+  if (IsOutOfProcessNetworkService()) {
+    default_factory_connection_error_handler_holder_.Bind(
+        std::move(factory_bundle->default_factory_info()));
+    default_factory_connection_error_handler_holder_->Clone(
+        mojo::MakeRequest(&factory_bundle->default_factory_info()));
+    default_factory_connection_error_handler_holder_
+        .set_connection_error_handler(base::BindOnce(
+            &EmbeddedSharedWorkerStub::Terminate, base::Unretained(this)));
+  }
 
-  // Make the factory bundle.
   subresource_loader_factories_ =
       base::MakeRefCounted<HostChildURLLoaderFactoryBundle>(
           impl_->GetTaskRunner(blink::TaskType::kInternalLoading));
-
-  // If NetworkService or S13nSW is enabled, the default factory must be
-  // given as a |factory_bundle|.
-  // In some tests |render_thread| could be null.
-  RenderThreadImpl* render_thread = RenderThreadImpl::current();
-  if (render_thread && !blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    subresource_loader_factories_->Update(
-        render_thread->blink_platform_impl()
-            ->CreateDefaultURLLoaderFactoryBundle()
-            ->PassInterface());
-  }
-
-  if (factory_bundle) {
-    // If the network service crashes, then self-destruct so clients don't get
-    // stuck with a worker with a broken loader. Self-destruction is effectively
-    // the same as the worker's process crashing.
-    if (IsOutOfProcessNetworkService()) {
-      default_factory_connection_error_handler_holder_.Bind(
-          std::move(factory_bundle->default_factory_info()));
-      default_factory_connection_error_handler_holder_->Clone(
-          mojo::MakeRequest(&factory_bundle->default_factory_info()));
-      default_factory_connection_error_handler_holder_
-          .set_connection_error_handler(base::BindOnce(
-              &EmbeddedSharedWorkerStub::Terminate, base::Unretained(this)));
-    }
-
-    subresource_loader_factories_->Update(
-        std::make_unique<ChildURLLoaderFactoryBundleInfo>(
-            std::move(factory_bundle)));
-  }
+  subresource_loader_factories_->Update(
+      std::make_unique<ChildURLLoaderFactoryBundleInfo>(
+          std::move(factory_bundle)));
 
   impl_->StartWorkerContext(
       url_, blink::WebString::FromUTF8(name_),
diff --git a/content/renderer/worker/embedded_shared_worker_stub.h b/content/renderer/worker/embedded_shared_worker_stub.h
index d5d248e..5fb382c 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.h
+++ b/content/renderer/worker/embedded_shared_worker_stub.h
@@ -127,8 +127,7 @@
   const int appcache_host_id_;
   WebApplicationCacheHostImpl* app_cache_host_ = nullptr;  // Not owned.
 
-  // S13nServiceWorker: The info needed to connect to the
-  // ServiceWorkerProviderHost on the browser.
+  // The info needed to connect to the ServiceWorkerProviderHost on the browser.
   blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
       service_worker_provider_info_;
 
@@ -139,8 +138,7 @@
   // NetworkService:
   blink::mojom::ControllerServiceWorkerInfoPtr controller_info_;
 
-  // S13nServiceWorker: The factory bundle used for loading subresources for
-  // this shared worker.
+  // The factory bundle used for loading subresources for this shared worker.
   scoped_refptr<HostChildURLLoaderFactoryBundle> subresource_loader_factories_;
 
   // NetworkService (PlzWorker): The response override parameters used for
diff --git a/content/renderer/worker/service_worker_network_provider_for_worker.cc b/content/renderer/worker/service_worker_network_provider_for_worker.cc
index 1106d554..f2cffab 100644
--- a/content/renderer/worker/service_worker_network_provider_for_worker.cc
+++ b/content/renderer/worker/service_worker_network_provider_for_worker.cc
@@ -29,47 +29,17 @@
     scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory,
     bool is_secure_context,
     std::unique_ptr<NavigationResponseOverrideParameters> response_override) {
+  DCHECK(info);
   auto provider = base::WrapUnique(new ServiceWorkerNetworkProviderForWorker(
       is_secure_context, std::move(response_override)));
-  if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    DCHECK(info);
-    provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
-        info->provider_id,
-        blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-        std::move(info->client_request), std::move(info->host_ptr_info),
-        std::move(controller_info), std::move(fallback_loader_factory));
-    if (script_loader_factory_info.is_valid()) {
-      provider->script_loader_factory_.Bind(
-          std::move(script_loader_factory_info));
-    }
-  } else {
-    DCHECK(!info);
-    int provider_id = ServiceWorkerProviderContext::GetNextId();
-    auto host_info = blink::mojom::ServiceWorkerProviderHostInfo::New(
-        provider_id, MSG_ROUTING_NONE,
-        blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-        true /* is_parent_frame_secure */, nullptr /* host_request */,
-        nullptr /* client_ptr_info */);
-    blink::mojom::ServiceWorkerContainerAssociatedRequest client_request =
-        mojo::MakeRequest(&host_info->client_ptr_info);
-    blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
-    host_info->host_request = mojo::MakeRequest(&host_ptr_info);
-
-    provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
-        provider_id, blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-        std::move(client_request), std::move(host_ptr_info),
-        std::move(controller_info), std::move(fallback_loader_factory));
-    if (ChildThreadImpl::current()) {
-      ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
-          &provider->dispatcher_host_);
-      provider->dispatcher_host_->OnProviderCreated(std::move(host_info));
-    } else {
-      // current() may be null in tests. Silently drop messages sent over
-      // ServiceWorkerContainerHost since we couldn't set it up correctly due
-      // to this test limitation. This way we don't crash when the associated
-      // interface ptr is used.
-      mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle());
-    }
+  provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
+      info->provider_id,
+      blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+      std::move(info->client_request), std::move(info->host_ptr_info),
+      std::move(controller_info), std::move(fallback_loader_factory));
+  if (script_loader_factory_info.is_valid()) {
+    provider->script_loader_factory_.Bind(
+        std::move(script_loader_factory_info));
   }
   return provider;
 }
@@ -107,11 +77,6 @@
     return nullptr;
   }
 
-  // S13nServiceWorker:
-  // We only install our own URLLoader if Servicification is enabled.
-  if (!blink::ServiceWorkerUtils::IsServicificationEnabled())
-    return nullptr;
-
   // If the |script_loader_factory_| exists, use it.
   if (script_loader_factory_) {
     RenderThreadImpl* render_thread = RenderThreadImpl::current();
diff --git a/content/renderer/worker/service_worker_network_provider_for_worker.h b/content/renderer/worker/service_worker_network_provider_for_worker.h
index 946f76c..fc8d6f29 100644
--- a/content/renderer/worker/service_worker_network_provider_for_worker.h
+++ b/content/renderer/worker/service_worker_network_provider_for_worker.h
@@ -26,8 +26,7 @@
 class ServiceWorkerNetworkProviderForWorker final
     : public blink::WebServiceWorkerNetworkProvider {
  public:
-  // Creates a new instance. Some params might only be used in S13nServiceWorker
-  // or PlzSharedWorker.
+  // Creates a new instance.
   // - |info|: provider info from the browser
   // - |script_loader_factory_info|: the factory for loading the worker's
   //   scripts
diff --git a/content/test/data/accessibility/event/live-region-change.html b/content/test/data/accessibility/event/live-region-change.html
index 9898d95b..caf272b 100644
--- a/content/test/data/accessibility/event/live-region-change.html
+++ b/content/test/data/accessibility/event/live-region-change.html
@@ -4,7 +4,7 @@
 <div id="live" aria-live="polite">Before</div>
 <script>
   function go() {
-    document.getElementById('live').innerText = 'After';
+    document.getElementById('live').firstChild.data = 'After';
   }
 </script>
 </body>
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 5309636..e96ecfc4 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3183,6 +3183,30 @@
       "features": [
         "disable_direct_composition_layers"
       ]
+    },
+    {
+      "id": 295,
+      "description": "Avoid waiting on a egl fence before swapping buffers and rely on implicit sync on Intel GPUs",
+      "cr_bugs": [938286],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": "Intel.*",
+      "features": [
+        "rely_on_implicit_sync_for_swap_buffers"
+      ]
+    },
+    {
+      "id": 296,
+      "description": "Avoid waiting on a egl fence before swapping buffers and rely on implicit sync on Broadcom GPUs",
+      "cr_bugs": [938286],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": "Broadcom.*",
+      "features": [
+        "rely_on_implicit_sync_for_swap_buffers"
+      ]
     }
   ]
 }
diff --git a/ios/chrome/app/application_delegate/user_activity_handler.mm b/ios/chrome/app/application_delegate/user_activity_handler.mm
index 018f840..46d4c86 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler.mm
@@ -23,10 +23,8 @@
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/metrics/first_user_action_recorder.h"
 #include "ios/chrome/browser/system_flags.h"
-#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
-#import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/u2f/u2f_controller.h"
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
 #import "ios/chrome/browser/ui/main/browser_interface_provider.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -237,13 +235,12 @@
   if ([startupInformation isPresentingFirstRunUI])
     return;
 
+  GURL externalURL = startupInformation.startupParameters.externalURL;
   // Check if it's an U2F call. If so, route it to correct tab.
   // If not, open or reuse tab in main BVC.
-  if ([U2FController
-          isU2FURL:[[startupInformation startupParameters] externalURL]]) {
-    [UserActivityHandler
-              routeU2FURL:[[startupInformation startupParameters] externalURL]
-        interfaceProvider:interfaceProvider];
+  if (U2FTabHelper::IsU2FUrl(externalURL)) {
+    [UserActivityHandler routeU2FURL:externalURL
+                   interfaceProvider:interfaceProvider];
     // It's OK to clear startup parameters here because routeU2FURL works
     // synchronously.
     [startupInformation setStartupParameters:nil];
@@ -270,7 +267,6 @@
     GURL URL;
     GURL virtualURL;
     GURL completeURL = startupInformation.startupParameters.completeURL;
-    GURL externalURL = startupInformation.startupParameters.externalURL;
     if (completeURL.SchemeIsFile() &&
         base::FeatureList::IsEnabled(
             experimental_flags::kExternalFilesLoadedInWebState)) {
@@ -342,7 +338,7 @@
 + (void)routeU2FURL:(const GURL&)URL
     interfaceProvider:(id<BrowserInterfaceProvider>)interfaceProvider {
   // Retrieve the designated TabID from U2F URL.
-  NSString* tabID = [U2FController tabIDFromResponseURL:URL];
+  NSString* tabID = U2FTabHelper::GetTabIdFromU2FUrl(URL);
   if (!tabID) {
     return;
   }
@@ -358,8 +354,7 @@
       web::WebState* webState = webStateList->GetWebStateAt(index);
       NSString* currentTabID = TabIdTabHelper::FromWebState(webState)->tab_id();
       if ([currentTabID isEqualToString:tabID]) {
-        Tab* tab = LegacyTabHelper::GetTabForWebState(webState);
-        [tab evaluateU2FResultFromURL:URL];
+        U2FTabHelper::FromWebState(webState)->EvaluateU2FResult(URL);
       }
     }
   }
diff --git a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
index 6a2ef7a..fc028fc 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
@@ -30,7 +30,7 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_observer.h"
-#import "ios/chrome/browser/u2f/u2f_controller.h"
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
 #import "ios/chrome/browser/ui/main/test/stub_browser_interface_provider.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h"
@@ -50,14 +50,27 @@
 #error "This file requires ARC support."
 #endif
 
-#pragma mark - Tab Mock
+// Substitutes U2FTabHelper for testing.
+class FakeU2FTabHelper : public U2FTabHelper {
+ public:
+  static void CreateForWebState(web::WebState* web_state) {
+    web_state->SetUserData(U2FTabHelper::UserDataKey(),
+                           base::WrapUnique(new FakeU2FTabHelper(web_state)));
+  }
+
+  void EvaluateU2FResult(const GURL& url) override { url_ = url; }
+
+  const GURL& url() const { return url_; }
+
+ private:
+  FakeU2FTabHelper(web::WebState* web_state) : U2FTabHelper(web_state) {}
+  GURL url_;
+  DISALLOW_COPY_AND_ASSIGN(FakeU2FTabHelper);
+};
 
 // Tab mock for using in UserActivity tests.
 @interface UserActivityHandlerTabMock : NSObject
 
-@property(nonatomic, readonly) GURL url;
-@property(nonatomic, readonly) NSString* tabId;
-
 - (instancetype)initWithWebState:(web::WebState*)webState
     NS_DESIGNATED_INITIALIZER;
 
@@ -69,8 +82,6 @@
   web::WebState* _webState;
 }
 
-@synthesize url = _url;
-
 - (instancetype)initWithWebState:(web::WebState*)webState {
   if ((self = [super init])) {
     DCHECK(webState);
@@ -79,12 +90,11 @@
   return self;
 }
 
-- (void)evaluateU2FResultFromURL:(const GURL&)url {
-  _url = url;
+- (FakeU2FTabHelper*)U2FTabHelper {
+  return static_cast<FakeU2FTabHelper*>(U2FTabHelper::FromWebState(_webState));
 }
 
 - (NSString*)tabId {
-  DCHECK(_webState);
   return TabIdTabHelper::FromWebState(_webState)->tab_id();
 }
 
@@ -112,6 +122,7 @@
 - (UserActivityHandlerTabMock*)addMockTab {
   auto testWebState = std::make_unique<web::TestWebState>();
   TabIdTabHelper::CreateForWebState(testWebState.get());
+  FakeU2FTabHelper::CreateForWebState(testWebState.get());
 
   UserActivityHandlerTabMock* tab =
       [[UserActivityHandlerTabMock alloc] initWithWebState:testWebState.get()];
@@ -617,7 +628,7 @@
 
   // Tests.
   EXPECT_OCMOCK_VERIFY(startupInformationMock);
-  EXPECT_EQ(gurl, tabMock.url);
+  EXPECT_EQ(gurl, [tabMock U2FTabHelper] -> url());
   EXPECT_TRUE(tabOpener.url.is_empty());
   EXPECT_TRUE(tabOpener.virtualURL.is_empty());
 }
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index f86c009..27ce382 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -390,10 +390,6 @@
     {"app-launcher-refresh", flag_descriptions::kAppLauncherRefreshName,
      flag_descriptions::kAppLauncherRefreshDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kAppLauncherRefresh)},
-    {"sync-standalone-transport",
-     flag_descriptions::kSyncStandaloneTransportName,
-     flag_descriptions::kSyncStandaloneTransportDescription, flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(switches::kSyncStandaloneTransport)},
     {"sync-support-secondary-account",
      flag_descriptions::kSyncSupportSecondaryAccountName,
      flag_descriptions::kSyncSupportSecondaryAccountDescription,
diff --git a/ios/chrome/browser/app_launcher/BUILD.gn b/ios/chrome/browser/app_launcher/BUILD.gn
index 96c7b5fe..21d636b 100644
--- a/ios/chrome/browser/app_launcher/BUILD.gn
+++ b/ios/chrome/browser/app_launcher/BUILD.gn
@@ -22,6 +22,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/u2f",
     "//ios/web/public",
     "//url",
   ]
@@ -56,6 +57,7 @@
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/reading_list",
     "//ios/chrome/browser/tabs",
+    "//ios/chrome/browser/u2f",
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/browser/web:web_internal",
     "//ios/web/public/test/fakes",
diff --git a/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm b/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm
index b9a384df..21a5fbb 100644
--- a/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm
+++ b/ios/chrome/browser/app_launcher/app_launcher_tab_helper.mm
@@ -17,6 +17,7 @@
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/url_scheme_util.h"
@@ -191,7 +192,6 @@
   if (!IsValidAppUrl(request_url))
     return false;
 
-  Tab* tab = LegacyTabHelper::GetTabForWebState(web_state_);
 
   // If this is a Universal 2nd Factor (U2F) call, the origin needs to be
   // checked to make sure it's secure and then update the |request_url| with
@@ -201,7 +201,8 @@
                       ->GetLastCommittedItem()
                       ->GetURL()
                       .GetOrigin();
-    request_url = [tab XCallbackFromRequestURL:request_url originURL:origin];
+    U2FTabHelper* u2f_helper = U2FTabHelper::FromWebState(web_state_);
+    request_url = u2f_helper->GetXCallbackUrl(request_url, origin);
     // If the URL was rejected by the U2F handler, |request_url| will be empty.
     if (!request_url.is_valid())
       return false;
@@ -215,6 +216,8 @@
   bool is_link_transition = ui::PageTransitionTypeIncludingQualifiersIs(
       request_info.transition_type, ui::PAGE_TRANSITION_LINK);
 
+  Tab* tab = LegacyTabHelper::GetTabForWebState(web_state_);
+
   if (base::FeatureList::IsEnabled(kAppLauncherRefresh)) {
     if (!is_link_transition && original_pending_url.is_valid()) {
       // At this stage the navigation will be canceled in all cases. If this
diff --git a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
index e71d928..27e66f6d 100644
--- a/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
+++ b/ios/chrome/browser/app_launcher/app_launcher_tab_helper_unittest.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/browser/chrome_url_util.h"
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #import "ios/web/public/test/fakes/test_navigation_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
@@ -112,6 +113,7 @@
         delegate_([[FakeAppLauncherTabHelperDelegate alloc] init]) {
     AppLauncherTabHelper::CreateForWebState(&web_state_, abuse_detector_,
                                             delegate_);
+    U2FTabHelper::CreateForWebState(&web_state_);
     // Allow is the default policy for this test.
     abuse_detector_.policy = ExternalAppLaunchPolicyAllow;
     auto navigation_manager = std::make_unique<FakeNavigationManager>();
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index d80bc62..05d6555 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -195,7 +195,7 @@
   void SetUp() override;
   void TearDown() override;
 
-  void SetUpForSuggestions(NSString* data);
+  void SetUpForSuggestions(NSString* data, size_t expected_number_of_forms);
 
   // Adds key value data to the Personal Data Manager and loads test page.
   void SetUpKeyValueData();
@@ -207,7 +207,13 @@
 
   // Blocks until |expected_size| forms have been fecthed.
   bool WaitForFormFetched(AutofillManager* manager,
-                          size_t expected_size) WARN_UNUSED_RESULT;
+                          size_t expected_number_of_forms) WARN_UNUSED_RESULT;
+
+  // Loads the page and wait until the initial form processing has been done.
+  // This processing must find |expected_size| forms.
+  bool LoadHtmlAndWaitForFormFetched(NSString* html,
+                                     size_t expected_number_of_forms)
+      WARN_UNUSED_RESULT;
 
   // Fails if the specified metric was not registered the given number of times.
   void ExpectMetric(const std::string& histogram_name, int sum);
@@ -303,14 +309,26 @@
   });
 }
 
-bool AutofillControllerTest::WaitForFormFetched(AutofillManager* manager,
-                                                size_t expected_size) {
+bool AutofillControllerTest::WaitForFormFetched(
+    AutofillManager* manager,
+    size_t expected_number_of_forms) {
   return base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForPageLoadTimeout, ^bool {
-        return manager->form_structures().size() == expected_size;
+        return manager->form_structures().size() == expected_number_of_forms;
       });
 }
 
+bool AutofillControllerTest::LoadHtmlAndWaitForFormFetched(
+    NSString* html,
+    size_t expected_number_of_forms) {
+  LoadHtml(html);
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
+  AutofillManager* autofill_manager =
+      AutofillDriverIOS::FromWebStateAndWebFrame(web_state(), main_frame)
+          ->autofill_manager();
+  return WaitForFormFetched(autofill_manager, expected_number_of_forms);
+}
+
 void AutofillControllerTest::ExpectMetric(const std::string& histogram_name,
                                           int sum) {
   histogram_tester_->ExpectBucketCount(histogram_name, sum, 1);
@@ -324,12 +342,11 @@
 // Checks that viewing an HTML page containing a form results in the form being
 // registered as a FormStructure by the AutofillManager.
 TEST_F(AutofillControllerTest, ReadForm) {
-  LoadHtml(kProfileFormHtml);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(kProfileFormHtml, 1));
   web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
   AutofillManager* autofill_manager =
       AutofillDriverIOS::FromWebStateAndWebFrame(web_state(), main_frame)
           ->autofill_manager();
-  EXPECT_TRUE(WaitForFormFetched(autofill_manager, 1));
   const auto& forms = autofill_manager->form_structures();
   const auto& form = *(forms.begin()->second);
   CheckField(form, NAME_FULL, "name_1");
@@ -345,12 +362,11 @@
 // the form being registered as a FormStructure by the AutofillManager, and the
 // name is correctly set.
 TEST_F(AutofillControllerTest, ReadFormName) {
-  LoadHtml(kMinimalFormWithNameHtml);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(kMinimalFormWithNameHtml, 1));
   web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
   AutofillManager* autofill_manager =
       AutofillDriverIOS::FromWebStateAndWebFrame(web_state(), main_frame)
           ->autofill_manager();
-  EXPECT_TRUE(WaitForFormFetched(autofill_manager, 1));
   const auto& forms = autofill_manager->form_structures();
   const auto& form = *(forms.begin()->second);
   EXPECT_EQ(base::UTF8ToUTF16("form1"), form.ToFormData().name);
@@ -365,7 +381,7 @@
           ios::ChromeBrowserState::FromBrowserState(GetBrowserState()));
   // Check there are no registered profiles already.
   EXPECT_EQ(0U, personal_data_manager->GetProfiles().size());
-  LoadHtml(kProfileFormHtml);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(kProfileFormHtml, 1));
   ExecuteJavaScript(@"document.forms[0].name.value = 'Homer Simpson'");
   ExecuteJavaScript(@"document.forms[0].address.value = '123 Main Street'");
   ExecuteJavaScript(@"document.forms[0].city.value = 'Springfield'");
@@ -392,7 +408,9 @@
             profile.GetInfo(AutofillType(ADDRESS_HOME_ZIP), "en-US"));
 }
 
-void AutofillControllerTest::SetUpForSuggestions(NSString* data) {
+void AutofillControllerTest::SetUpForSuggestions(
+    NSString* data,
+    size_t expected_number_of_forms) {
   PersonalDataManager* personal_data_manager =
       PersonalDataManagerFactory::GetForBrowserState(
           ios::ChromeBrowserState::FromBrowserState(GetBrowserState()));
@@ -406,7 +424,7 @@
   personal_data_manager->SaveImportedProfile(profile);
   EXPECT_EQ(1U, personal_data_manager->GetProfiles().size());
 
-  LoadHtml(data);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(data, expected_number_of_forms));
   WaitForBackgroundTasks();
 }
 
@@ -414,7 +432,7 @@
 // suggestions being sent to the AutofillAgent, once data has been loaded into a
 // test data manager.
 TEST_F(AutofillControllerTest, ProfileSuggestions) {
-  SetUpForSuggestions(kProfileFormHtml);
+  SetUpForSuggestions(kProfileFormHtml, 1);
   ForceViewRendering(web_state()->GetView());
   ExecuteJavaScript(@"document.forms[0].name.focus()");
   WaitForSuggestionRetrieval(/*wait_for_trigger=*/YES);
@@ -429,7 +447,8 @@
 // there is another anonymous form on the page.
 TEST_F(AutofillControllerTest, ProfileSuggestionsTwoAnonymousForms) {
   SetUpForSuggestions(
-      [NSString stringWithFormat:@"%@%@", kProfileFormHtml, kProfileFormHtml]);
+      [NSString stringWithFormat:@"%@%@", kProfileFormHtml, kProfileFormHtml],
+      2);
   ForceViewRendering(web_state()->GetView());
   ExecuteJavaScript(@"document.forms[0].name.focus()");
   WaitForSuggestionRetrieval(/*wait_for_trigger=*/YES);
@@ -444,7 +463,7 @@
 // in suggestions being sent to the AutofillAgent, once data has been loaded
 // into a test data manager.
 TEST_F(AutofillControllerTest, ProfileSuggestionsFromSelectField) {
-  SetUpForSuggestions(kProfileFormHtml);
+  SetUpForSuggestions(kProfileFormHtml, 1);
   ForceViewRendering(web_state()->GetView());
   ExecuteJavaScript(@"document.forms[0].state.focus()");
   WaitForSuggestionRetrieval(/*wait_for_trigger=*/YES);
@@ -483,9 +502,7 @@
   profile2.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16("94043"));
   personal_data_manager->SaveImportedProfile(profile2);
   EXPECT_EQ(2U, personal_data_manager->GetProfiles().size());
-  LoadHtml(kProfileFormHtml);
-  base::TaskScheduler::GetInstance()->FlushForTesting();
-  WaitForBackgroundTasks();
+  EXPECT_TRUE(LoadHtmlAndWaitForFormFetched(kProfileFormHtml, 1));
   ForceViewRendering(web_state()->GetView());
   ExecuteJavaScript(@"document.forms[0].name.focus()");
   WaitForSuggestionRetrieval(/*wait_for_trigger=*/YES);
@@ -498,7 +515,7 @@
 // with scripts (simulating user form submission) results in data being
 // successfully registered.
 TEST_F(AutofillControllerTest, KeyValueImport) {
-  LoadHtml(kKeyValueFormHtml);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(kKeyValueFormHtml, 1));
   ExecuteJavaScript(@"document.forms[0].greeting.value = 'Hello'");
   scoped_refptr<AutofillWebDataService> web_data_service =
       ios::WebDataServiceFactory::GetAutofillWebDataForBrowserState(
@@ -540,7 +557,7 @@
   web_data_service->AddFormFields(values);
 
   // Load test page.
-  LoadHtml(kKeyValueFormHtml);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(kKeyValueFormHtml, 1));
   WaitForBackgroundTasks();
 }
 
@@ -631,7 +648,7 @@
 
   // Check there are no registered profiles already.
   EXPECT_EQ(0U, personal_data_manager->GetCreditCards().size());
-  LoadHtml(kCreditCardFormHtml);
+  ASSERT_TRUE(LoadHtmlAndWaitForFormFetched(kCreditCardFormHtml, 1));
   ExecuteJavaScript(@"document.forms[0].name.value = 'Superman'");
   ExecuteJavaScript(@"document.forms[0].CCNo.value = '4000-4444-4444-4444'");
   ExecuteJavaScript(@"document.forms[0].CCExpiresMonth.value = '11'");
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 434cc98..d9d3d6ea 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -121,12 +121,6 @@
 const char kSyncSandboxDescription[] =
     "Connects to the testing server for Chrome Sync.";
 
-const char kSyncStandaloneTransportName[] = "Allow Sync standalone transport";
-const char kSyncStandaloneTransportDescription[] =
-    "If enabled, allows Chrome Sync to start in standalone transport mode. In "
-    "this mode, the Sync machinery can start without user opt-in, but only a "
-    "subset of data types are supported.";
-
 const char kSyncSupportSecondaryAccountName[] =
     "Support secondary accounts for Sync standalone transport";
 const char kSyncSupportSecondaryAccountDescription[] =
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 64319c5..00a2007c 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -93,11 +93,6 @@
 extern const char kSyncSandboxName[];
 extern const char kSyncSandboxDescription[];
 
-// Title and description for the flag to control if Chrome Sync can start up in
-// standalone transport mode.
-extern const char kSyncStandaloneTransportName[];
-extern const char kSyncStandaloneTransportDescription[];
-
 // Title and description for the flag to control if Chrome Sync (in standalone
 // transport mode) supports non-primary accounts.
 extern const char kSyncSupportSecondaryAccountName[];
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index 9a7bbd291..a29d9a3 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -402,9 +402,9 @@
   // Open sync encryption menu.
   [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"kSettingsSyncId")]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(
-                                          l10n_util::GetNSStringWithFixup(
-                                              IDS_IOS_SYNC_ENCRYPTION_TITLE))]
+  [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
+
+                                          IDS_IOS_SYNC_ENCRYPTION_TITLE)]
       performAction:grey_tap()];
   // Select passphrase encryption.
   [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
diff --git a/ios/chrome/browser/payments/payment_request_util.h b/ios/chrome/browser/payments/payment_request_util.h
index c2e8377..a91e732 100644
--- a/ios/chrome/browser/payments/payment_request_util.h
+++ b/ios/chrome/browser/payments/payment_request_util.h
@@ -11,16 +11,13 @@
 #include <vector>
 
 #include "base/strings/string16.h"
+#include "base/values.h"
 #include "components/payments/core/payment_options_provider.h"
 
 namespace autofill {
 class AutofillProfile;
 }  // namespace autofill
 
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
 namespace payments {
 class PaymentInstrument;
 class PaymentRequest;
@@ -29,9 +26,9 @@
 
 namespace payment_request_util {
 
-// Returns a base::DictionaryValue populated with the properties of |response|.
-std::unique_ptr<base::DictionaryValue> PaymentResponseToDictionaryValue(
-    const payments::PaymentResponse& response);
+// Returns a base::Value populated with the properties of |response|. The
+// returned value is always a dictionary.
+base::Value PaymentResponseToValue(const payments::PaymentResponse& response);
 
 // Helper function to create a name label from an autofill profile. Returns nil
 // if the resulting label is empty.
diff --git a/ios/chrome/browser/payments/payment_request_util.mm b/ios/chrome/browser/payments/payment_request_util.mm
index 4a00ee9..aecf6768 100644
--- a/ios/chrome/browser/payments/payment_request_util.mm
+++ b/ios/chrome/browser/payments/payment_request_util.mm
@@ -46,28 +46,28 @@
 
 }  // namespace
 
-std::unique_ptr<base::DictionaryValue> PaymentResponseToDictionaryValue(
-    const payments::PaymentResponse& response) {
-  auto result = std::make_unique<base::DictionaryValue>();
-  result->SetString(kPaymentResponseId, response.payment_request_id);
-  result->SetString(kPaymentResponseMethodName, response.method_name);
+base::Value PaymentResponseToValue(const payments::PaymentResponse& response) {
+  base::Value result(base::Value::Type::DICTIONARY);
+  result.SetKey(kPaymentResponseId, base::Value(response.payment_request_id));
+  result.SetKey(kPaymentResponseMethodName, base::Value(response.method_name));
   // |details| is a json-serialized string. Parse it to a base::Value so that
   // when |result| is converted to a JSON string, the "details" property won't
   // get json-escaped.
-  std::unique_ptr<base::Value> details_value =
-      base::JSONReader::ReadDeprecated(response.details);
-  result->Set(kPaymentResponseDetails, details_value
-                                           ? std::move(details_value)
-                                           : std::make_unique<base::Value>());
-  result->Set(kPaymentResponseShippingAddress,
-              response.shipping_address
-                  ? payments::PaymentAddressToDictionaryValue(
-                        *response.shipping_address)
-                  : std::make_unique<base::Value>());
-  result->SetString(kPaymentResponseShippingOption, response.shipping_option);
-  result->SetString(kPaymentResponsePayerName, response.payer_name);
-  result->SetString(kPaymentResponsePayerEmail, response.payer_email);
-  result->SetString(kPaymentResponsePayerPhone, response.payer_phone);
+  base::Optional<base::Value> details_value =
+      base::JSONReader::Read(response.details);
+  result.SetKey(kPaymentResponseDetails,
+                std::move(details_value).value_or(base::Value()));
+  result.SetKey(kPaymentResponseShippingAddress,
+                response.shipping_address
+                    ? base::Value::FromUniquePtrValue(
+                          payments::PaymentAddressToDictionaryValue(
+                              *response.shipping_address))
+                    : base::Value());
+  result.SetKey(kPaymentResponseShippingOption,
+                base::Value(response.shipping_option));
+  result.SetKey(kPaymentResponsePayerName, base::Value(response.payer_name));
+  result.SetKey(kPaymentResponsePayerEmail, base::Value(response.payer_email));
+  result.SetKey(kPaymentResponsePayerPhone, base::Value(response.payer_phone));
   return result;
 }
 
diff --git a/ios/chrome/browser/payments/payment_request_util_unittest.mm b/ios/chrome/browser/payments/payment_request_util_unittest.mm
index 72dfeea3..5d77fd3 100644
--- a/ios/chrome/browser/payments/payment_request_util_unittest.mm
+++ b/ios/chrome/browser/payments/payment_request_util_unittest.mm
@@ -21,65 +21,70 @@
 // Tests that serializing a default PaymentResponse yields the expected result.
 TEST_F(PaymentRequestUtilTest,
        PaymentResponseToDictionaryValue_EmptyResponseDictionary) {
-  base::DictionaryValue expected_value;
+  base::Value expected_value(base::Value::Type::DICTIONARY);
 
-  expected_value.SetString("requestId", "");
-  expected_value.SetString("methodName", "");
-  expected_value.Set("details", std::make_unique<base::Value>());
-  expected_value.Set("shippingAddress", std::make_unique<base::Value>());
-  expected_value.SetString("shippingOption", "");
-  expected_value.SetString("payerName", "");
-  expected_value.SetString("payerEmail", "");
-  expected_value.SetString("payerPhone", "");
+  expected_value.SetKey("requestId", base::Value(base::Value::Type::STRING));
+  expected_value.SetKey("methodName", base::Value(base::Value::Type::STRING));
+  expected_value.SetKey("details", base::Value());
+  expected_value.SetKey("shippingAddress", base::Value());
+  expected_value.SetKey("shippingOption",
+                        base::Value(base::Value::Type::STRING));
+  expected_value.SetKey("payerName", base::Value(base::Value::Type::STRING));
+  expected_value.SetKey("payerEmail", base::Value(base::Value::Type::STRING));
+  expected_value.SetKey("payerPhone", base::Value(base::Value::Type::STRING));
 
   payments::PaymentResponse payment_response;
-  EXPECT_TRUE(expected_value.Equals(
-      PaymentResponseToDictionaryValue(payment_response).get()));
+  EXPECT_EQ(expected_value, PaymentResponseToValue(payment_response));
 }
 
 // Tests that serializing a populated PaymentResponse yields the expected
 // result.
 TEST_F(PaymentRequestUtilTest,
        PaymentResponseToDictionaryValue_PopulatedResponseDictionary) {
-  base::DictionaryValue expected_value;
+  base::Value expected_value(base::Value::Type::DICTIONARY);
 
-  auto details = std::make_unique<base::DictionaryValue>();
-  details->SetString("cardNumber", "1111-1111-1111-1111");
-  details->SetString("cardholderName", "Jon Doe");
-  details->SetString("expiryMonth", "02");
-  details->SetString("expiryYear", "2090");
-  details->SetString("cardSecurityCode", "111");
-  auto billing_address = std::make_unique<base::DictionaryValue>();
-  billing_address->SetString("country", "");
-  billing_address->Set("addressLine", std::make_unique<base::ListValue>());
-  billing_address->SetString("region", "");
-  billing_address->SetString("dependentLocality", "");
-  billing_address->SetString("city", "");
-  billing_address->SetString("postalCode", "90210");
-  billing_address->SetString("sortingCode", "");
-  billing_address->SetString("organization", "");
-  billing_address->SetString("recipient", "");
-  billing_address->SetString("phone", "");
-  details->Set("billingAddress", std::move(billing_address));
-  expected_value.Set("details", std::move(details));
-  expected_value.SetString("requestId", "12345");
-  expected_value.SetString("methodName", "American Express");
-  auto shipping_address = std::make_unique<base::DictionaryValue>();
-  shipping_address->SetString("country", "");
-  shipping_address->Set("addressLine", std::make_unique<base::ListValue>());
-  shipping_address->SetString("region", "");
-  shipping_address->SetString("dependentLocality", "");
-  shipping_address->SetString("city", "");
-  shipping_address->SetString("postalCode", "94115");
-  shipping_address->SetString("sortingCode", "");
-  shipping_address->SetString("organization", "");
-  shipping_address->SetString("recipient", "");
-  shipping_address->SetString("phone", "");
-  expected_value.Set("shippingAddress", std::move(shipping_address));
-  expected_value.SetString("shippingOption", "666");
-  expected_value.SetString("payerName", "Jane Doe");
-  expected_value.SetString("payerEmail", "jane@example.com");
-  expected_value.SetString("payerPhone", "1234-567-890");
+  base::Value details(base::Value::Type::DICTIONARY);
+  details.SetKey("cardNumber", base::Value("1111-1111-1111-1111"));
+  details.SetKey("cardholderName", base::Value("Jon Doe"));
+  details.SetKey("expiryMonth", base::Value("02"));
+  details.SetKey("expiryYear", base::Value("2090"));
+  details.SetKey("cardSecurityCode", base::Value("111"));
+  base::Value billing_address(base::Value::Type::DICTIONARY);
+  billing_address.SetKey("country", base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("addressLine", base::Value(base::Value::Type::LIST));
+  billing_address.SetKey("region", base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("dependentLocality",
+                         base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("city", base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("postalCode", base::Value("90210"));
+  billing_address.SetKey("sortingCode", base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("organization",
+                         base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("recipient", base::Value(base::Value::Type::STRING));
+  billing_address.SetKey("phone", base::Value(base::Value::Type::STRING));
+  details.SetKey("billingAddress", std::move(billing_address));
+  expected_value.SetKey("details", std::move(details));
+  expected_value.SetKey("requestId", base::Value("12345"));
+  expected_value.SetKey("methodName", base::Value("American Express"));
+  base::Value shipping_address(base::Value::Type::DICTIONARY);
+  shipping_address.SetKey("country", base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("addressLine", base::Value(base::Value::Type::LIST));
+  shipping_address.SetKey("region", base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("dependentLocality",
+                          base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("city", base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("postalCode", base::Value("94115"));
+  shipping_address.SetKey("sortingCode",
+                          base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("organization",
+                          base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("recipient", base::Value(base::Value::Type::STRING));
+  shipping_address.SetKey("phone", base::Value(base::Value::Type::STRING));
+  expected_value.SetKey("shippingAddress", std::move(shipping_address));
+  expected_value.SetKey("shippingOption", base::Value("666"));
+  expected_value.SetKey("payerName", base::Value("Jane Doe"));
+  expected_value.SetKey("payerEmail", base::Value("jane@example.com"));
+  expected_value.SetKey("payerPhone", base::Value("1234-567-890"));
 
   payments::PaymentResponse payment_response;
   payment_response.payment_request_id = "12345";
@@ -106,8 +111,7 @@
   payment_response.payer_name = base::ASCIIToUTF16("Jane Doe");
   payment_response.payer_email = base::ASCIIToUTF16("jane@example.com");
   payment_response.payer_phone = base::ASCIIToUTF16("1234-567-890");
-  EXPECT_TRUE(expected_value.Equals(
-      PaymentResponseToDictionaryValue(payment_response).get()));
+  EXPECT_EQ(expected_value, PaymentResponseToValue(payment_response));
 }
 
 }  // namespace payment_request_util
diff --git a/ios/chrome/browser/signin/identity_service_creator.cc b/ios/chrome/browser/signin/identity_service_creator.cc
index 47e228f..a67f597 100644
--- a/ios/chrome/browser/signin/identity_service_creator.cc
+++ b/ios/chrome/browser/signin/identity_service_creator.cc
@@ -9,8 +9,6 @@
 #include "components/signin/core/browser/signin_manager.h"
 #include "ios/chrome/browser/signin/account_tracker_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
-#include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "services/identity/identity_service.h"
 
 std::unique_ptr<service_manager::Service> CreateIdentityService(
@@ -20,8 +18,6 @@
       IdentityManagerFactory::GetForBrowserState(browser_state);
   AccountTrackerService* account_tracker =
       ios::AccountTrackerServiceFactory::GetForBrowserState(browser_state);
-  ProfileOAuth2TokenService* token_service =
-      ProfileOAuth2TokenServiceFactory::GetForBrowserState(browser_state);
   return std::make_unique<identity::IdentityService>(
-      identity_manager, account_tracker, token_service, std::move(request));
+      identity_manager, account_tracker, std::move(request));
 }
diff --git a/ios/chrome/browser/tabs/tab.h b/ios/chrome/browser/tabs/tab.h
index 14e4043..7162082 100644
--- a/ios/chrome/browser/tabs/tab.h
+++ b/ios/chrome/browser/tabs/tab.h
@@ -103,15 +103,6 @@
 // Called before capturing a snapshot for Tab.
 - (void)willUpdateSnapshot;
 
-// Evaluates U2F result.
-- (void)evaluateU2FResultFromURL:(const GURL&)url;
-
-// Generates a GURL compliant with the x-callback-url specs for FIDO Universal
-// 2nd Factory (U2F) requests. Returns empty GURL if origin is not secure.
-// See http://x-callback-url.com/specifications/ for specifications.
-- (GURL)XCallbackFromRequestURL:(const GURL&)requestURL
-                      originURL:(const GURL&)originURL;
-
 // Sends a notification to indicate that |url| is going to start loading.
 - (void)notifyTabOfUrlMayStartLoading:(const GURL&)url;
 
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 561a56c5..727fe254 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -60,7 +60,6 @@
 #import "ios/chrome/browser/tabs/tab_helper_util.h"
 #import "ios/chrome/browser/tabs/tab_private.h"
 #include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
-#import "ios/chrome/browser/u2f/u2f_controller.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
 #import "ios/chrome/browser/ui/open_in_controller.h"
@@ -131,9 +130,6 @@
   // Allows Tab to conform CRWWebStateDelegate protocol.
   std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
 
-  // Universal Second Factor (U2F) call controller.
-  U2FController* _secondFactorController;
-
 }
 
 // Returns the OpenInController for this tab.
@@ -245,27 +241,6 @@
   }
 }
 
-#pragma mark - Public API (relating to U2F)
-
-- (void)evaluateU2FResultFromURL:(const GURL&)URL {
-  DCHECK(_secondFactorController);
-  [_secondFactorController evaluateU2FResultFromU2FURL:URL
-                                              webState:self.webState];
-}
-
-- (GURL)XCallbackFromRequestURL:(const GURL&)requestURL
-                      originURL:(const GURL&)originURL {
-  // Create U2FController object lazily.
-  if (!_secondFactorController)
-    _secondFactorController = [[U2FController alloc] init];
-  return [_secondFactorController
-      XCallbackFromRequestURL:requestURL
-                    originURL:originURL
-                       tabURL:self.webState->GetLastCommittedURL()
-                        tabID:TabIdTabHelper::FromWebState(self.webState)
-                                  ->tab_id()];
-}
-
 #pragma mark - CRWWebStateObserver protocol
 
 - (void)webState:(web::WebState*)webState
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index 97a2fc6..db87f14 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -45,6 +45,7 @@
 #import "ios/chrome/browser/tabs/legacy_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/voice/voice_search_navigations_tab_helper.h"
@@ -85,6 +86,7 @@
   IOSSecurityStateTabHelper::CreateForWebState(web_state);
   BlockedPopupTabHelper::CreateForWebState(web_state);
   FindTabHelper::CreateForWebState(web_state);
+  U2FTabHelper::CreateForWebState(web_state);
   StoreKitTabHelper::CreateForWebState(web_state);
   JavaScriptConsoleTabHelper::CreateForWebState(tab.webState);
   ITunesUrlsHandlerTabHelper::CreateForWebState(web_state);
diff --git a/ios/chrome/browser/translate/BUILD.gn b/ios/chrome/browser/translate/BUILD.gn
index 65dfe36..0de815d 100644
--- a/ios/chrome/browser/translate/BUILD.gn
+++ b/ios/chrome/browser/translate/BUILD.gn
@@ -95,12 +95,7 @@
   ]
 }
 
-# Those tests do not use external URLs but fail if the network is unavailable
-# as they explicitly check whether the network is up or not; it is not possible
-# to safely instantiate a net::NetworkChangeNotifier::DisableForTest to disable
-# those checks as this in not thread-safe. See http://crbug.com/709131 for more
-# details.
-source_set("external_url_eg_tests") {
+source_set("eg_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
diff --git a/ios/chrome/browser/translate/translate_egtest.mm b/ios/chrome/browser/translate/translate_egtest.mm
index 78e5d12..f4724555 100644
--- a/ios/chrome/browser/translate/translate_egtest.mm
+++ b/ios/chrome/browser/translate/translate_egtest.mm
@@ -36,6 +36,7 @@
 #include "ios/web/public/test/http_server/data_response_provider.h"
 #import "ios/web/public/test/http_server/http_server.h"
 #include "ios/web/public/test/http_server/http_server_util.h"
+#include "net/base/network_change_notifier.h"
 #include "net/base/url_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -237,6 +238,28 @@
       base::StringPrintf("<html><body>%s</body></html>", kLanguagePathText);
 }
 
+// Simulates a given network connection type for tests.
+// TODO(crbug.com/938598): Refactor this and similar net::NetworkChangeNotifier
+// subclasses for testing into a separate file.
+class FakeNetworkChangeNotifier : public net::NetworkChangeNotifier {
+ public:
+  FakeNetworkChangeNotifier(
+      net::NetworkChangeNotifier::ConnectionType connection_type_to_return)
+      : connection_type_to_return_(connection_type_to_return) {}
+
+ private:
+  ConnectionType GetCurrentConnectionType() const override {
+    return connection_type_to_return_;
+  }
+
+  // The currently simulated network connection type. If this is set to
+  // CONNECTION_NONE, then NetworkChangeNotifier::IsOffline will return true.
+  net::NetworkChangeNotifier::ConnectionType connection_type_to_return_ =
+      net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeNetworkChangeNotifier);
+};
+
 }  // namespace
 
 using chrome_test_util::TapWebViewElementWithId;
@@ -302,6 +325,9 @@
 @interface TranslateTestCase : ChromeTestCase {
   std::unique_ptr<translate::LanguageDetectionDetails>
       _language_detection_details;
+  std::unique_ptr<net::NetworkChangeNotifier::DisableForTest>
+      network_change_notifier_disabler_;
+  std::unique_ptr<FakeNetworkChangeNotifier> network_change_notifier_;
 }
 @end
 
@@ -316,6 +342,13 @@
             std::make_unique<translate::LanguageDetectionDetails>(details);
       });
   SetTestingLanguageDetectionCallback(copyDetailsCallback);
+
+  // Disable the net::NetworkChangeNotifier singleton and replace it with a
+  // FakeNetworkChangeNotifier to simulate a WIFI network connection.
+  network_change_notifier_disabler_ =
+      std::make_unique<net::NetworkChangeNotifier::DisableForTest>();
+  network_change_notifier_ = std::make_unique<FakeNetworkChangeNotifier>(
+      net::NetworkChangeNotifier::CONNECTION_WIFI);
 }
 
 - (void)tearDown {
diff --git a/ios/chrome/browser/u2f/BUILD.gn b/ios/chrome/browser/u2f/BUILD.gn
index bfb6bab..7cebd98 100644
--- a/ios/chrome/browser/u2f/BUILD.gn
+++ b/ios/chrome/browser/u2f/BUILD.gn
@@ -4,6 +4,23 @@
 
 source_set("u2f") {
   sources = [
+    "u2f_tab_helper.h",
+    "u2f_tab_helper.mm",
+  ]
+  deps = [
+    ":u2f_internal",
+    "//base",
+    "//ios/chrome/browser/web:tab_id_tab_helper",
+    "//ios/web",
+    "//net",
+    "//url",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("u2f_internal") {
+  sources = [
     "u2f_controller.h",
     "u2f_controller.mm",
   ]
@@ -17,7 +34,6 @@
     "//net",
     "//url",
   ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
@@ -25,13 +41,17 @@
   testonly = true
   sources = [
     "u2f_controller_unittest.mm",
+    "u2f_tab_helper_unittest.mm",
   ]
   deps = [
     ":u2f",
+    ":u2f_internal",
     "//base",
     "//ios/chrome/browser",
+    "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/web",
     "//ios/web/public/test/fakes",
+    "//net",
     "//testing/gmock",
     "//testing/gtest",
     "//url",
diff --git a/ios/chrome/browser/u2f/u2f_controller.h b/ios/chrome/browser/u2f/u2f_controller.h
index fa1f6fb..d2357097 100644
--- a/ios/chrome/browser/u2f/u2f_controller.h
+++ b/ios/chrome/browser/u2f/u2f_controller.h
@@ -14,14 +14,10 @@
 }  // namespace web
 
 // This class provides methods needed to handle FIDO U2F calls.
+// U2FController shouldn't be used outside of this directory. Instead all users
+// should use U2FTabHelper.
 @interface U2FController : NSObject
 
-// Checks if the given URL is U2F callback.
-+ (BOOL)isU2FURL:(const GURL&)URL;
-
-// Returns the tabID in the U2F callback. Returns nil if tabID not found.
-+ (NSString*)tabIDFromResponseURL:(const GURL&)URL;
-
 // Generates the x-callback GURL for making FIDO U2F requests. Returns empty
 // GURL if origin is not secure.
 - (GURL)XCallbackFromRequestURL:(const GURL&)requestURL
diff --git a/ios/chrome/browser/u2f/u2f_controller.mm b/ios/chrome/browser/u2f/u2f_controller.mm
index e55fe043..ef3e9715d 100644
--- a/ios/chrome/browser/u2f/u2f_controller.mm
+++ b/ios/chrome/browser/u2f/u2f_controller.mm
@@ -61,22 +61,6 @@
   return self;
 }
 
-+ (BOOL)isU2FURL:(const GURL&)URL {
-  std::string isU2F;
-  if (net::GetValueForKeyInQuery(URL, std::string(kIsU2FKey), &isU2F)) {
-    return isU2F == "1";
-  }
-  return NO;
-}
-
-+ (NSString*)tabIDFromResponseURL:(const GURL&)URL {
-  std::string tabID;
-  if (net::GetValueForKeyInQuery(URL, std::string(kTabIDKey), &tabID)) {
-    return base::SysUTF8ToNSString(tabID);
-  }
-  return nil;
-}
-
 - (GURL)XCallbackFromRequestURL:(const GURL&)requestURL
                       originURL:(const GURL&)originURL
                          tabURL:(const GURL&)tabURL
diff --git a/ios/chrome/browser/u2f/u2f_controller_unittest.mm b/ios/chrome/browser/u2f/u2f_controller_unittest.mm
index 0442e9e..d561373 100644
--- a/ios/chrome/browser/u2f/u2f_controller_unittest.mm
+++ b/ios/chrome/browser/u2f/u2f_controller_unittest.mm
@@ -64,32 +64,6 @@
   U2FController* _U2FController;
 };
 
-TEST_F(U2FControllerTest, IsU2FURLTest) {
-  GURL U2FURL("chromium://u2f-callback?isU2F=1");
-  EXPECT_TRUE([U2FController isU2FURL:U2FURL]);
-
-  GURL wrongU2FURL("chromium://u2f-callback?isU2F=0");
-  EXPECT_FALSE([U2FController isU2FURL:wrongU2FURL]);
-
-  GURL nonU2FURL("chromium://u2f-callback");
-  EXPECT_FALSE([U2FController isU2FURL:nonU2FURL]);
-
-  GURL invalidURL;
-  EXPECT_FALSE([U2FController isU2FURL:invalidURL]);
-}
-
-TEST_F(U2FControllerTest, TabIDFromResponseURLTest) {
-  NSString* tabID = @"B05B1860-18BA-43EA-B7DC-470D9F918FF5";
-  GURL correctURL(
-      "chromium://"
-      "u2f-callback?tabID=B05B1860-18BA-43EA-B7DC-470D9F918FF5");
-  EXPECT_TRUE(
-      [[U2FController tabIDFromResponseURL:correctURL] isEqualToString:tabID]);
-
-  GURL wrongURL("chromium://u2fdemo.appspot.com");
-  EXPECT_EQ(nil, [U2FController tabIDFromResponseURL:wrongURL]);
-}
-
 TEST_F(U2FControllerTest, XCallbackFromRequestURLWithCorrectFlowTest) {
   // Test when request is legal and properly formatted.
   GURL requestURL("u2f://accounts.google.com?data=abc&def%26ghi");
diff --git a/ios/chrome/browser/u2f/u2f_tab_helper.h b/ios/chrome/browser/u2f/u2f_tab_helper.h
new file mode 100644
index 0000000..984cbd1
--- /dev/null
+++ b/ios/chrome/browser/u2f/u2f_tab_helper.h
@@ -0,0 +1,53 @@
+// 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_U2F_U2F_TAB_HELPER_H_
+#define IOS_CHROME_BROWSER_U2F_U2F_TAB_HELPER_H_
+
+#include "base/macros.h"
+#import "ios/web/public/web_state/web_state_user_data.h"
+#include "url/gurl.h"
+
+@class U2FController;
+
+// A tab helper that handles Universal 2nd Factory (U2F) requests.
+class U2FTabHelper : public web::WebStateUserData<U2FTabHelper> {
+ public:
+  ~U2FTabHelper() override;
+
+  // Checks if the given |url| is U2F call URL.
+  static bool IsU2FUrl(const GURL& url);
+
+  // Returns the tabID in the U2F callback. Returns nil if tabID not found.
+  static NSString* GetTabIdFromU2FUrl(const GURL& u2f_url);
+
+  // Evaluates U2F result.
+  virtual void EvaluateU2FResult(const GURL& url);
+
+  // Generates a GURL compliant with the x-callback-url specs for FIDO Universal
+  // 2nd Factory (U2F) requests. Returns empty GURL if origin is not secure.
+  // See http://x-callback-url.com/specifications/ for specifications.
+  // This function is needed used in App launching to verify the |request_url|
+  // before launching the required authentication app.
+  GURL GetXCallbackUrl(const GURL& request_url, const GURL& origin_url);
+
+ protected:
+  // Constructor for U2FTabHelper.
+  U2FTabHelper(web::WebState* web_state);
+
+ private:
+  friend class web::WebStateUserData<U2FTabHelper>;
+
+  // The WebState that this object is attached to.
+  web::WebState* web_state_ = nullptr;
+
+  // Universal Second Factor (U2F) call controller.
+  U2FController* second_factor_controller_ = nil;
+
+  WEB_STATE_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(U2FTabHelper);
+};
+
+#endif  // IOS_CHROME_BROWSER_U2F_U2F_TAB_HELPER_H_
diff --git a/ios/chrome/browser/u2f/u2f_tab_helper.mm b/ios/chrome/browser/u2f/u2f_tab_helper.mm
new file mode 100644
index 0000000..2218c0e
--- /dev/null
+++ b/ios/chrome/browser/u2f/u2f_tab_helper.mm
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
+
+#import "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/u2f/u2f_controller.h"
+#import "ios/chrome/browser/web/tab_id_tab_helper.h"
+#import "ios/web/public/web_state/web_state.h"
+#include "net/base/url_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const char kIsU2FKey[] = "isU2F";
+const char kTabIDKey[] = "tabID";
+}  // namespace
+
+U2FTabHelper::U2FTabHelper(web::WebState* web_state) : web_state_(web_state) {}
+
+U2FTabHelper::~U2FTabHelper() = default;
+
+bool U2FTabHelper::IsU2FUrl(const GURL& url) {
+  std::string is_u2f;
+  return net::GetValueForKeyInQuery(url, kIsU2FKey, &is_u2f) && is_u2f == "1";
+}
+
+NSString* U2FTabHelper::GetTabIdFromU2FUrl(const GURL& url) {
+  std::string tab_id;
+  if (net::GetValueForKeyInQuery(url, kTabIDKey, &tab_id)) {
+    return base::SysUTF8ToNSString(tab_id);
+  }
+  return nil;
+}
+
+void U2FTabHelper::EvaluateU2FResult(const GURL& url) {
+  DCHECK(second_factor_controller_);
+  [second_factor_controller_ evaluateU2FResultFromU2FURL:url
+                                                webState:web_state_];
+}
+
+GURL U2FTabHelper::GetXCallbackUrl(const GURL& request_url,
+                                   const GURL& origin_url) {
+  // Create U2FController object lazily.
+  if (!second_factor_controller_)
+    second_factor_controller_ = [[U2FController alloc] init];
+  NSString* tab_id = TabIdTabHelper::FromWebState(web_state_)->tab_id();
+  return [second_factor_controller_
+      XCallbackFromRequestURL:request_url
+                    originURL:origin_url
+                       tabURL:web_state_->GetLastCommittedURL()
+                        tabID:tab_id];
+}
+
+WEB_STATE_USER_DATA_KEY_IMPL(U2FTabHelper)
diff --git a/ios/chrome/browser/u2f/u2f_tab_helper_unittest.mm b/ios/chrome/browser/u2f/u2f_tab_helper_unittest.mm
new file mode 100644
index 0000000..b09d2a94
--- /dev/null
+++ b/ios/chrome/browser/u2f/u2f_tab_helper_unittest.mm
@@ -0,0 +1,286 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/u2f/u2f_tab_helper.h"
+
+#import <UIKit/UIKit.h>
+
+#import "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#import "ios/chrome/browser/chrome_url_util.h"
+#import "ios/chrome/browser/web/tab_id_tab_helper.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "ios/web/public/web_state/url_verification_constants.h"
+#include "net/base/escape.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+class U2FTabHelperTest : public PlatformTest {
+ protected:
+  U2FTabHelperTest() {
+    U2FTabHelper::CreateForWebState(&web_state_);
+    TabIdTabHelper::CreateForWebState(&web_state_);
+    url::AddStandardScheme("chromium", url::SCHEME_WITH_HOST);
+    [[ChromeAppConstants sharedInstance]
+        setCallbackSchemeForTesting:@"chromium"];
+  }
+
+  U2FTabHelper* tab_helper() { return U2FTabHelper::FromWebState(&web_state_); }
+
+  NSString* tab_id() {
+    return TabIdTabHelper::FromWebState(&web_state_)->tab_id();
+  }
+
+  // Returns the requestUUID NSString from a properly formatted U2F XCallback
+  // GURL.
+  NSString* GetRequestUuidFromXCallbackUrl(const GURL& xcallback_url) {
+    NSString* regex_string = @".+success.+requestUUID%3D(.+)%26.+error.+";
+    NSRegularExpression* regex =
+        [NSRegularExpression regularExpressionWithPattern:regex_string
+                                                  options:0
+                                                    error:nil];
+    NSString* url_string = base::SysUTF8ToNSString(xcallback_url.spec());
+    NSArray* matches =
+        [regex matchesInString:url_string
+                       options:0
+                         range:NSMakeRange(0, url_string.length)];
+    EXPECT_EQ(1u, [matches count]);
+    NSString* request_uuid_string = [regex
+        stringByReplacingMatchesInString:url_string
+                                 options:0
+                                   range:NSMakeRange(0, url_string.length)
+                            withTemplate:@"$1"];
+    DCHECK(request_uuid_string.length);
+    return request_uuid_string;
+  }
+
+  // Returns Regular experssion string that match a correct XCallback URL.
+  NSString* GetRegexString(const GURL& request_url, const GURL& origin_url) {
+    return [@[
+      @"u2f-x-callback://x-callback-url/auth\\?x-success=.+u2f-callback",
+      @"%2F%3FtabID%3D", tab_id(),
+      @"%26requestUUID%3.+%26isU2F%3D1&x-error=.+u2f-callback%2F%3FtabID%3D",
+      tab_id(), @"%26requestUUID%3.+%26isU2F%3D1&data=",
+      base::SysUTF8ToNSString(
+          net::EscapeQueryParamValue(request_url.query(), true)),
+      @"&origin=",
+      base::SysUTF8ToNSString(
+          net::EscapeQueryParamValue(origin_url.spec(), true))
+    ] componentsJoinedByString:@""];
+  }
+
+  web::TestWebState web_state_;
+};
+
+// Tests that IsU2FUrl returns true only if U2F url param is true.
+TEST_F(U2FTabHelperTest, TestIsU2FUrl) {
+  GURL u2f_url("chromium://u2f-callback?isU2F=1");
+  EXPECT_TRUE(U2FTabHelper::IsU2FUrl(u2f_url));
+
+  GURL wrong_u2f_url("chromium://u2f-callback?isU2F=0");
+  EXPECT_FALSE(U2FTabHelper::IsU2FUrl(wrong_u2f_url));
+
+  GURL non_u2f_url("chromium://u2f-callback");
+  EXPECT_FALSE(U2FTabHelper::IsU2FUrl(non_u2f_url));
+
+  GURL invalid_url;
+  EXPECT_FALSE(U2FTabHelper::IsU2FUrl(invalid_url));
+}
+
+// Tests that GetTabIdFromU2FUrl returns the correct tab ID.
+TEST_F(U2FTabHelperTest, TestGetTabIdFromU2FURL) {
+  NSString* tab_id = @"B05B1860-18BA-43EA-B7DC-470D9F918FF5";
+  GURL correct_url("chromium://"
+                   "u2f-callback?tabID=B05B1860-18BA-43EA-B7DC-470D9F918FF5");
+  EXPECT_NSEQ(tab_id, U2FTabHelper::GetTabIdFromU2FUrl(correct_url));
+
+  GURL wrong_url("chromium://u2fdemo.appspot.com");
+  EXPECT_FALSE(U2FTabHelper::GetTabIdFromU2FUrl(wrong_url));
+}
+
+// Tests when request is legal and properly formatted.
+TEST_F(U2FTabHelperTest, TestGetXCallbackUrlWithCorrectFlow) {
+  GURL tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(tab_url);
+  GURL request_url("u2f://accounts.google.com?data=abc&def%26ghi");
+  GURL origin_url("https://accounts.google.com");
+  GURL xcallback_url = tab_helper()->GetXCallbackUrl(request_url, origin_url);
+
+  NSRegularExpression* regex = [NSRegularExpression
+      regularExpressionWithPattern:GetRegexString(request_url, origin_url)
+                           options:0
+                             error:nil];
+
+  NSArray* matches =
+      [regex matchesInString:base::SysUTF8ToNSString(xcallback_url.spec())
+                     options:0
+                       range:NSMakeRange(0, xcallback_url.spec().length())];
+  EXPECT_EQ(1u, [matches count]);
+}
+
+// Tests when request is legal but contains duplicated parameters.
+TEST_F(U2FTabHelperTest, TestGetXCallbackUrlWithDuplicatedParams) {
+  GURL request_url("chromium://u2f-callback?isU2F=0&tabID=1&requestUUID=2"
+                   "&data=abc&def%26ghi");
+  GURL origin_url("https://accounts.google.com");
+  GURL tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(tab_url);
+  GURL xcallback_url = tab_helper()->GetXCallbackUrl(request_url, origin_url);
+
+  NSRegularExpression* regex = [NSRegularExpression
+      regularExpressionWithPattern:GetRegexString(request_url, origin_url)
+                           options:0
+                             error:nil];
+
+  NSArray* matches =
+      [regex matchesInString:base::SysUTF8ToNSString(xcallback_url.spec())
+                     options:0
+                       range:NSMakeRange(0, xcallback_url.spec().length())];
+  EXPECT_EQ(1u, [matches count]);
+}
+
+// Tests when request site is not whitelisted.
+TEST_F(U2FTabHelperTest, TestGetXCallbackUrlWithNonWhitelistedUrl) {
+  GURL request_url("u2f://accounts.google.com?data=abc&def%26ghi");
+  GURL evil_origin_url("https://evil.appspot.com");
+  GURL tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(tab_url);
+  GURL evil_xcallback_url =
+      tab_helper()->GetXCallbackUrl(request_url, evil_origin_url);
+  EXPECT_EQ(GURL(), evil_xcallback_url);
+}
+
+// Tests when request site does not have secure connection.
+TEST_F(U2FTabHelperTest, TestGetXCallbackUrlWithInsecureConnection) {
+  GURL request_url("u2f://accounts.google.com?data=abc&def%26ghi");
+  GURL insecure_origin_url("http://accounts.google.com");
+  GURL tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(tab_url);
+  GURL insecure_xcallback_url =
+      tab_helper()->GetXCallbackUrl(request_url, insecure_origin_url);
+  EXPECT_EQ(GURL(), insecure_xcallback_url);
+}
+
+// Tests when U2F callback has correct information, Tab URL has not changed and
+// is trusted.
+TEST_F(U2FTabHelperTest, TestEvaluateU2FResultWithCorrectFlowTest) {
+  GURL request_url("u2f://accounts.google.com?data=abc");
+  GURL origin_url("https://accounts.google.com");
+  GURL tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(tab_url);
+  GURL xcallback_url = tab_helper()->GetXCallbackUrl(request_url, origin_url);
+  NSString* request_uuid = GetRequestUuidFromXCallbackUrl(xcallback_url);
+  web_state_.SetTrustLevel(web::URLVerificationTrustLevel::kAbsolute);
+  GURL correct_request_uuid_url(
+      "chromium://u2f-callback?requestUUID=" +
+      base::SysNSStringToUTF8(request_uuid) +
+      "&requestId=TestID&registrationData=TestData&tabID=" +
+      base::SysNSStringToUTF8(tab_id()));
+
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  tab_helper()->EvaluateU2FResult(correct_request_uuid_url);
+  std::string last_executed_js =
+      base::UTF16ToUTF8(web_state_.GetLastExecutedJavascript());
+  EXPECT_EQ(0, static_cast<int>(last_executed_js.find("u2f.callbackMap_")));
+
+  // Test Replay Attack - Subsequent calls with the
+  // same requestUUID should not do anything.
+  web_state_.ClearLastExecutedJavascript();
+  tab_helper()->EvaluateU2FResult(correct_request_uuid_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+}
+
+// Tests when U2F callback is not formatted correctly.
+TEST_F(U2FTabHelperTest, TestEvaluateU2FResultWithBadURLFormat) {
+  GURL request_url("u2f://accounts.google.com?data=abc");
+  GURL origin_url("https://accounts.google.com");
+  GURL tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(tab_url);
+  GURL xcallback_url = tab_helper()->GetXCallbackUrl(request_url, origin_url);
+  NSString* request_uuid = GetRequestUuidFromXCallbackUrl(xcallback_url);
+  web_state_.SetTrustLevel(web::URLVerificationTrustLevel::kAbsolute);
+
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  // Test when U2F callback has no requestUUID info.
+  GURL no_request_uuid_url(
+      "chromium://"
+      "u2f-callback?requestId=TestID&registrationData=TestData&tabID=" +
+      base::SysNSStringToUTF8(tab_id()));
+  tab_helper()->EvaluateU2FResult(no_request_uuid_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  // Test when U2F callback has wrong requestUUID value.
+  GURL wrong_request_uuid_url("chromium://"
+                              "u2f-callback?requestId=TestID&registrationData="
+                              "TestData&requestUUID=123&tabID=" +
+                              base::SysNSStringToUTF8(tab_id()));
+  tab_helper()->EvaluateU2FResult(wrong_request_uuid_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  // Test when U2F callback has no registrationData value.
+  GURL no_registration_request_url(
+      "chromium://u2f-callback?requestUUID=" +
+      base::SysNSStringToUTF8(request_uuid) +
+      "&requestId=TestID&tabID=" + base::SysNSStringToUTF8(tab_id()));
+
+  tab_helper()->EvaluateU2FResult(no_registration_request_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  // Test when U2F callback hostname is unexpected.
+  GURL wrong_host_name_url(
+      "chromium://"
+      "evil-callback?requestId=TestID&registrationData=TestData&requestUUID=" +
+      base::SysNSStringToUTF8(request_uuid) +
+      "&tabID=" + base::SysNSStringToUTF8(tab_id()));
+  tab_helper()->EvaluateU2FResult(wrong_host_name_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+}
+
+// Tests when last committed URL is not valid for U2F.
+TEST_F(U2FTabHelperTest, TestEvaluateU2FResultWithBadTabState) {
+  GURL request_url("u2f://accounts.google.com?data=abc");
+  GURL origin_url("https://accounts.google.com");
+  GURL correct_tab_url("https://accounts.google.com");
+  web_state_.SetCurrentURL(correct_tab_url);
+  GURL xcallback_url = tab_helper()->GetXCallbackUrl(request_url, origin_url);
+  NSString* request_uuid = GetRequestUuidFromXCallbackUrl(xcallback_url);
+
+  // Verify that last executed javascript is empty.
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  // Test when U2F callback has correct information but Tab URL changed.
+  web_state_.SetTrustLevel(web::URLVerificationTrustLevel::kAbsolute);
+  web_state_.SetCurrentURL(GURL("http://www.dummy.com"));
+  GURL correct_request_uuid_url(
+      "chromium://"
+      "u2f-callback?requestId=TestID&registrationData=TestData&requestUUID=" +
+      base::SysNSStringToUTF8(request_uuid) +
+      "&tabID=" + base::SysNSStringToUTF8(tab_id()));
+
+  tab_helper()->EvaluateU2FResult(correct_request_uuid_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+
+  // Test when U2F callback has correct information but Tab URL not trusted.
+  web_state_.SetTrustLevel(web::URLVerificationTrustLevel::kNone);
+  web_state_.SetCurrentURL(correct_tab_url);
+  xcallback_url = tab_helper()->GetXCallbackUrl(request_url, origin_url);
+  request_uuid = GetRequestUuidFromXCallbackUrl(xcallback_url);
+  correct_request_uuid_url = GURL(
+      "chromium://"
+      "u2f-callback?requestId=TestID&registrationData=TestData&requestUUID=" +
+      base::SysNSStringToUTF8(request_uuid) +
+      "&tabID=" + base::SysNSStringToUTF8(tab_id()));
+
+  tab_helper()->EvaluateU2FResult(correct_request_uuid_url);
+  EXPECT_TRUE(web_state_.GetLastExecutedJavascript().empty());
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 2336142..d5186a2 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -123,6 +123,7 @@
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/ntp",
+    "//ios/chrome/browser/ui/ntp_tile_views",
     "//ios/chrome/browser/ui/omnibox:omnibox_internal",
     "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
     "//ios/chrome/browser/ui/overscroll_actions",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm
index fd52a90e..46c8ae4 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_cell.mm
@@ -6,7 +6,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_constants.h"
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/favicon/favicon_view.h"
@@ -53,7 +53,8 @@
 }
 
 + (CGSize)defaultSize {
-  return MostVisitedCellSize();
+  return MostVisitedCellSize(
+      UIApplication.sharedApplication.preferredContentSizeCategory);
 }
 
 - (CGSize)intrinsicContentSize {
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
index 0ab6181..2e7e2fc 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
@@ -7,7 +7,7 @@
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_constants.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/common/favicon/favicon_view.h"
 #import "ios/chrome/common/material_timing.h"
@@ -60,7 +60,8 @@
 }
 
 + (CGSize)defaultSize {
-  return MostVisitedCellSize();
+  return MostVisitedCellSize(
+      UIApplication.sharedApplication.preferredContentSizeCategory);
 }
 
 - (CGSize)intrinsicContentSize {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
index 673ccee..243871850 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
@@ -13,13 +13,6 @@
 
 extern const CGFloat kHintTextScale;
 
-// Returns the spacing between tiles, based on the device.
-CGFloat horizontalSpacingBetweenTiles();
-CGFloat verticalSpacingBetweenTiles();
-
-// Returns x-offset in order to have the tiles centered in a view with a
-// |width|.
-CGFloat centeredTilesMarginForWidth(CGFloat width);
 // Returns the proper height for the doodle. |logoIsShowing| refers to the
 // Google logo or the doodle.
 CGFloat doodleHeight(BOOL logoIsShowing);
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
index d535a96..6084acb6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -10,7 +10,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.h"
 #import "ios/chrome/browser/ui/location_bar/location_bar_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
-#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/ui/util/dynamic_type_util.h"
@@ -26,11 +25,6 @@
 
 namespace {
 
-// Spacing between tiles.
-const CGFloat kHorizontalSpacingRegularXRegular = 19;
-const CGFloat kHorizontalSpacingOther = 9;
-const CGFloat kVerticalSpacing = 16;
-
 // Width of search field.
 const CGFloat kSearchFieldLarge = 432;
 const CGFloat kSearchFieldSmall = 343;
@@ -72,32 +66,6 @@
 const int kSearchFieldBackgroundColor = 0xF1F3F4;
 const CGFloat kHintTextScale = 0.15;
 
-CGFloat horizontalSpacingBetweenTiles() {
-  return (!IsCompactWidth() && !IsCompactHeight())
-             ? kHorizontalSpacingRegularXRegular
-             : kHorizontalSpacingOther;
-}
-
-CGFloat verticalSpacingBetweenTiles() {
-  return kVerticalSpacing;
-}
-
-CGFloat centeredTilesMarginForWidth(CGFloat width) {
-  CGFloat horizontalSpace = horizontalSpacingBetweenTiles();
-  NSUInteger columns = NumberOfTilesPerRow();
-  CGFloat whitespace =
-      width -
-      (columns * [ContentSuggestionsMostVisitedCell defaultSize].width) -
-      ((columns - 1) * horizontalSpace);
-  CGFloat margin = AlignValueToPixel(whitespace / 2);
-  // Allow for less spacing as an edge case on smaller devices.
-  if (margin < horizontalSpace) {
-    DCHECK(width < 400);  // For now this is only expected on small widths.
-    return fmaxf(margin, 0);
-  }
-  return margin;
-}
-
 CGFloat doodleHeight(BOOL logoIsShowing) {
   if (!IsRegularXRegularSizeClass() && !logoIsShowing)
     return kNonGoogleSearchDoodleHeight;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
index 2db6728..5b96bce 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -87,22 +87,6 @@
   std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler_;
 };
 
-TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPhone6) {
-  // Setup.
-  SetAsIPhone();
-
-  CGFloat result = centeredTilesMarginForWidth(375);
-  EXPECT_EQ(28, result);
-}
-
-TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPad) {
-  // Setup.
-  SetAsIPad();
-
-  CGFloat result = centeredTilesMarginForWidth(767);
-  EXPECT_EQ(209, result);
-}
-
 TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPad) {
   // Setup.
   SetAsIPad();
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index e6df5f4..b39b7dd 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -431,8 +432,8 @@
     parentInset.right = 0;
   } else if ([self.collectionUpdater isMostVisitedSection:section] ||
              [self.collectionUpdater isPromoSection:section]) {
-    CGFloat margin = content_suggestions::centeredTilesMarginForWidth(
-        collectionView.frame.size.width);
+    CGFloat margin = CenteredTilesMarginForWidth(
+        self.traitCollection, collectionView.frame.size.width);
     parentInset.left = margin;
     parentInset.right = margin;
     if ([self.collectionUpdater isMostVisitedSection:section]) {
@@ -455,7 +456,7 @@
                                             collectionViewLayout
     minimumLineSpacingForSectionAtIndex:(NSInteger)section {
   if ([self.collectionUpdater isMostVisitedSection:section]) {
-    return content_suggestions::verticalSpacingBetweenTiles();
+    return kNtpTilesVerticalSpacing;
   }
   return [super collectionView:collectionView
                                    layout:collectionViewLayout
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
index de0f27b..684c109 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
@@ -9,6 +9,7 @@
 #import "ios/chrome/browser/ui/location_bar/extended_touch_target_button.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
+#import "ios/chrome/browser/ui/util/dynamic_type_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index 7aaf050..d178525 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -476,6 +476,10 @@
 
       [menu setTargetRect:self.locationBarSteadyView.frame inView:self.view];
       [menu setMenuVisible:YES animated:YES];
+      // When we present the menu manually, it doesn't get focused by Voiceover.
+      // This notification forces voiceover to select the presented menu.
+      UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification,
+                                      menu);
     });
   }
 }
diff --git a/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn b/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn
index 4746cd2..228fd5e 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp_tile_views/BUILD.gn
@@ -9,6 +9,8 @@
     "ntp_most_visited_tile_view.mm",
     "ntp_shortcut_tile_view.h",
     "ntp_shortcut_tile_view.mm",
+    "ntp_tile_layout_util.h",
+    "ntp_tile_layout_util.mm",
     "ntp_tile_view.h",
     "ntp_tile_view.mm",
   ]
@@ -19,6 +21,7 @@
     "resources:ntp_most_visited_tile",
     "resources:ntp_readinglist_icon",
     "resources:ntp_recent_icon",
+    "//base",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/favicon",
     "//ios/chrome/common/ui_util",
@@ -26,6 +29,20 @@
   ]
 }
 
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "ntp_tile_layout_util_unittest.mm",
+  ]
+  deps = [
+    ":ntp_tile_views",
+    "//base",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
 source_set("constants") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h
index 96912a4..46844b5 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h
+++ b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h
@@ -15,12 +15,6 @@
   NTPCollectionShortcutTypeHistory,
 };
 
-// Returns the size of most visited cell according to current font size.
-CGSize MostVisitedCellSize();
-
-// Returns number of tiles per row based on current system font size.
-NSUInteger NumberOfTilesPerRow();
-
 // Returns a localized title for a given collection shortcut type.
 NSString* TitleForCollectionShortcutType(NTPCollectionShortcutType action);
 // Returns an icon for a given collection shortcut type to be used in an NTP
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm
index 7eaf6a2f2..489261b 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm
+++ b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.mm
@@ -13,47 +13,6 @@
 #error "This file requires ARC support."
 #endif
 
-// For font size < UIContentSizeCategoryExtraExtraExtraLarge
-const CGSize kMostVisitedCellSizeSmall = {/*width=*/73, /*height=*/100};
-// For font size == UIContentSizeCategoryExtraExtraExtraLarge
-const CGSize kMostVisitedCellSizeMedium = {/*width=*/73, /*height=*/112};
-// For font size == UIContentSizeCategoryAccessibilityMedium
-const CGSize kMostVisitedCellSizeLarge = {/*width=*/110, /*height=*/140};
-// For font size > UIContentSizeCategoryAccessibilityMedium
-const CGSize kMostVisitedCellSizeExtraLarge = {/*width=*/146, /*height=*/150};
-
-CGSize MostVisitedCellSize() {
-  UIContentSizeCategory category =
-      UIApplication.sharedApplication.preferredContentSizeCategory;
-  NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
-      category, UIContentSizeCategoryAccessibilityMedium);
-  switch (result) {
-    case NSOrderedAscending:
-      return ([category
-                 isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])
-                 ? kMostVisitedCellSizeMedium
-                 : kMostVisitedCellSizeSmall;
-    case NSOrderedSame:
-      return kMostVisitedCellSizeLarge;
-    case NSOrderedDescending:
-      return kMostVisitedCellSizeExtraLarge;
-  }
-}
-
-NSUInteger NumberOfTilesPerRow() {
-  NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
-      UIApplication.sharedApplication.preferredContentSizeCategory,
-      UIContentSizeCategoryAccessibilityMedium);
-  switch (result) {
-    case NSOrderedAscending:
-      return 4;
-    case NSOrderedSame:
-      return 3;
-    case NSOrderedDescending:
-      return 2;
-  }
-}
-
 // Returns the title to use for a cell with |action|.
 NSString* TitleForCollectionShortcutType(NTPCollectionShortcutType type) {
   switch (type) {
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h
new file mode 100644
index 0000000..ff376b22
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_LAYOUT_UTIL_H_
+#define IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_LAYOUT_UTIL_H_
+
+#import <UIKit/UIKit.h>
+
+// Vertical spacing between rows of tiles.
+extern const int kNtpTilesVerticalSpacing;
+// Vertical spacing between columns of tiles.
+extern const int kNtpTilesHorizontalSpacingRegular;
+extern const int kNtpTilesHorizontalSpacingCompact;
+
+// For font size < UIContentSizeCategoryExtraExtraExtraLarge.
+extern const CGSize kNtpTileViewSizeSmall;
+// For font size == UIContentSizeCategoryExtraExtraExtraLarge.
+extern const CGSize kNtpTileViewSizeMedium;
+// For font size == UIContentSizeCategoryAccessibilityMedium.
+extern const CGSize kNtpTileViewSizeLarge;
+// For font size > UIContentSizeCategoryAccessibilityMedium.
+extern const CGSize kNtpTileViewSizeExtraLarge;
+
+// Returns the vertical spacing between columns of tiles under
+// |trait_collection|.
+CGFloat NtpTilesHorizontalSpacing(UITraitCollection* trait_collection);
+
+// Returns the size of most visited cell based on |category|.
+CGSize MostVisitedCellSize(UIContentSizeCategory category);
+
+// Returns x-offset in order to have the tiles centered in a view with a
+// |width| under |environment|.
+CGFloat CenteredTilesMarginForWidth(UITraitCollection* trait_collection,
+                                    CGFloat width);
+
+#endif  // IOS_CHROME_BROWSER_UI_NTP_TILE_VIEWS_NTP_TILE_LAYOUT_UTIL_H_
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.mm b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.mm
new file mode 100644
index 0000000..48c17c8
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.mm
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+const int kNtpTilesVerticalSpacing = 16;
+const int kNtpTilesHorizontalSpacingRegular = 19;
+const int kNtpTilesHorizontalSpacingCompact = 5;
+
+const CGSize kNtpTileViewSizeSmall = {/*width=*/73, /*height=*/100};
+const CGSize kNtpTileViewSizeMedium = {/*width=*/73, /*height=*/112};
+const CGSize kNtpTileViewSizeLarge = {/*width=*/110, /*height=*/140};
+const CGSize kNtpTileViewSizeExtraLarge = {/*width=*/146, /*height=*/150};
+
+namespace {
+// Display at most 4 tiles per row.
+const int kMaxNumberOfTilesPerRow = 4;
+}
+
+CGFloat NtpTilesHorizontalSpacing(UITraitCollection* trait_collection) {
+  return (trait_collection.horizontalSizeClass !=
+              UIUserInterfaceSizeClassCompact &&
+          trait_collection.verticalSizeClass != UIUserInterfaceSizeClassCompact)
+             ? kNtpTilesHorizontalSpacingRegular
+             : kNtpTilesHorizontalSpacingCompact;
+}
+
+CGSize MostVisitedCellSize(UIContentSizeCategory category) {
+  NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
+      category, UIContentSizeCategoryAccessibilityMedium);
+  switch (result) {
+    case NSOrderedAscending:
+      return ([category
+                 isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])
+                 ? kNtpTileViewSizeMedium
+                 : kNtpTileViewSizeSmall;
+    case NSOrderedSame:
+      return kNtpTileViewSizeLarge;
+    case NSOrderedDescending:
+      return kNtpTileViewSizeExtraLarge;
+  }
+}
+
+CGFloat CenteredTilesMarginForWidth(UITraitCollection* trait_collection,
+                                    CGFloat width) {
+  CGFloat horizontalSpace = NtpTilesHorizontalSpacing(trait_collection);
+  CGSize cellSize =
+      MostVisitedCellSize(trait_collection.preferredContentSizeCategory);
+  for (int columns = kMaxNumberOfTilesPerRow; columns > 0; --columns) {
+    CGFloat whitespace =
+        width - (columns * cellSize.width) - ((columns - 1) * horizontalSpace);
+    CGFloat margin = AlignValueToPixel(whitespace / 2);
+    if (margin >= horizontalSpace) {
+      return margin;
+    }
+  }
+  NOTREACHED();
+  return 0;
+}
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util_unittest.mm b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util_unittest.mm
new file mode 100644
index 0000000..b0bd4980
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util_unittest.mm
@@ -0,0 +1,126 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
+
+#import <UIKit/UIKit.h>
+
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using NtpTileLayoutUtilTest = PlatformTest;
+
+// Tests that MostVisitedCellSize returns correct size for all content size
+// categories.
+TEST_F(NtpTileLayoutUtilTest, MostVisitedCellSize) {
+  EXPECT_TRUE(
+      CGSizeEqualToSize(kNtpTileViewSizeSmall,
+                        MostVisitedCellSize(UIContentSizeCategoryUnspecified)));
+  EXPECT_TRUE(
+      CGSizeEqualToSize(kNtpTileViewSizeSmall,
+                        MostVisitedCellSize(UIContentSizeCategoryExtraSmall)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategorySmall)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategoryMedium)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeSmall, MostVisitedCellSize(UIContentSizeCategoryLarge)));
+  EXPECT_TRUE(
+      CGSizeEqualToSize(kNtpTileViewSizeSmall,
+                        MostVisitedCellSize(UIContentSizeCategoryExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeSmall,
+      MostVisitedCellSize(UIContentSizeCategoryExtraExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeMedium,
+      MostVisitedCellSize(UIContentSizeCategoryExtraExtraExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityMedium)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeExtraLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeExtraLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeExtraLarge,
+      MostVisitedCellSize(UIContentSizeCategoryAccessibilityExtraExtraLarge)));
+  EXPECT_TRUE(CGSizeEqualToSize(
+      kNtpTileViewSizeExtraLarge,
+      MostVisitedCellSize(
+          UIContentSizeCategoryAccessibilityExtraExtraExtraLarge)));
+}
+
+// Tests that CenteredTilesMarginForWidth works under various environment.
+TEST_F(NtpTileLayoutUtilTest, CenteredTilesMarginForWidth) {
+  // Set up Regular size class and Large font size.
+  UITraitCollection* trait_collection =
+      [UITraitCollection traitCollectionWithTraitsFromCollections:@[
+        [UITraitCollection traitCollectionWithHorizontalSizeClass:
+                               UIUserInterfaceSizeClassRegular],
+        [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
+                               UIContentSizeCategoryLarge]
+      ]];
+
+  // Display 4 columns on very big screen.
+  EXPECT_EQ(200, CenteredTilesMarginForWidth(
+                     trait_collection,
+                     kNtpTileViewSizeSmall.width * 4 +
+                         kNtpTilesHorizontalSpacingRegular * 3 + 200 * 2));
+  // Display 4 columns on normal screen.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeSmall.width * 4 +
+                        kNtpTilesHorizontalSpacingRegular * 3 + 20 * 2));
+  // Display 3 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeSmall.width * 3 +
+                        kNtpTilesHorizontalSpacingRegular * 2 + 20 * 2));
+  // Display 2 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeSmall.width * 2 +
+                        kNtpTilesHorizontalSpacingRegular * 1 + 20 * 2));
+  // Display 1 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeSmall.width * 1 +
+                        kNtpTilesHorizontalSpacingRegular * 0 + 20 * 2));
+
+  // Set up Compact size class and Accessibility Large font size.
+  trait_collection =
+      [UITraitCollection traitCollectionWithTraitsFromCollections:@[
+        [UITraitCollection traitCollectionWithHorizontalSizeClass:
+                               UIUserInterfaceSizeClassCompact],
+        [UITraitCollection traitCollectionWithPreferredContentSizeCategory:
+                               UIContentSizeCategoryAccessibilityLarge]
+      ]];
+
+  // Display 4 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeExtraLarge.width * 4 +
+                        kNtpTilesHorizontalSpacingCompact * 3 + 20 * 2));
+  // Display 3 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeExtraLarge.width * 3 +
+                        kNtpTilesHorizontalSpacingCompact * 2 + 20 * 2));
+  // Display 2 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeExtraLarge.width * 2 +
+                        kNtpTilesHorizontalSpacingCompact * 1 + 20 * 2));
+  // Display 1 columns.
+  EXPECT_EQ(20, CenteredTilesMarginForWidth(
+                    trait_collection,
+                    kNtpTileViewSizeExtraLarge.width * 1 +
+                        kNtpTilesHorizontalSpacingCompact * 0 + 20 * 2));
+}
diff --git a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm
index 87a22315..4379e528 100644
--- a/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm
+++ b/ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.mm
@@ -4,7 +4,7 @@
 
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_view.h"
 
-#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/ui/util/dynamic_type_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
index acc5102..b09ae95 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.mm
@@ -21,6 +21,7 @@
 #import "ios/chrome/browser/ui/toolbar/public/features.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #import "ios/chrome/browser/ui/util/animation_util.h"
+#import "ios/chrome/browser/ui/util/dynamic_type_util.h"
 #import "ios/chrome/browser/ui/util/reversed_animation.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
diff --git a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
index b5ceba5..b559c57 100644
--- a/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_most_visited_tile_view.h"
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_shortcut_tile_view.h"
 #import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_constants.h"
+#import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h"
 #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/collection_shortcut_cell.h"
 #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/most_visited_shortcut_cell.h"
 #import "ios/chrome/browser/ui/omnibox/popup/shortcuts/shortcuts_view_controller_delegate.h"
@@ -24,8 +25,6 @@
 
 namespace {
 const NSInteger kNumberOfItemsPerRow = 4;
-const CGFloat kLineSpacing = 30;
-const CGFloat kItemSpacing = 10;
 const CGFloat kTopInset = 10;
 
 const NSInteger kMostVisitedSection = 0;
@@ -72,10 +71,8 @@
 - (void)viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   // Calculate insets to center the items in the view.
-  CGFloat widthInsets = (self.view.bounds.size.width -
-                         MostVisitedCellSize().width * NumberOfTilesPerRow() -
-                         kItemSpacing * (kNumberOfItemsPerRow - 1)) /
-                        2;
+  CGFloat widthInsets = CenteredTilesMarginForWidth(
+      self.traitCollection, self.view.bounds.size.width);
   self.layout.sectionInset =
       UIEdgeInsetsMake(kTopInset, widthInsets, 0, widthInsets);
 }
@@ -108,8 +105,11 @@
   }
 
   _layout = [[UICollectionViewFlowLayout alloc] init];
-  _layout.minimumLineSpacing = kLineSpacing;
-  _layout.itemSize = MostVisitedCellSize();
+  _layout.minimumLineSpacing = kNtpTilesVerticalSpacing;
+  _layout.minimumInteritemSpacing =
+      NtpTilesHorizontalSpacing(self.traitCollection);
+  _layout.itemSize =
+      MostVisitedCellSize(self.traitCollection.preferredContentSizeCategory);
   return _layout;
 }
 
diff --git a/ios/chrome/browser/ui/payments/js_payment_request_manager.mm b/ios/chrome/browser/ui/payments/js_payment_request_manager.mm
index be8a633..96a776f7 100644
--- a/ios/chrome/browser/ui/payments/js_payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/js_payment_request_manager.mm
@@ -69,10 +69,10 @@
             (const payments::PaymentResponse&)paymentResponse
                                completionHandler:
                                    (ProceduralBlockWithBool)completionHandler {
-  std::unique_ptr<base::DictionaryValue> paymentResponseData =
-      payment_request_util::PaymentResponseToDictionaryValue(paymentResponse);
+  base::Value paymentResponseData =
+      payment_request_util::PaymentResponseToValue(paymentResponse);
   std::string paymentResponseDataJSON;
-  base::JSONWriter::Write(*paymentResponseData, &paymentResponseDataJSON);
+  base::JSONWriter::Write(paymentResponseData, &paymentResponseDataJSON);
   NSString* script = [NSString
       stringWithFormat:
           @"__gCrWeb['paymentRequestManager'].resolveRequestPromise(%@)",
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 38e33b1..52dc9bc 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -88,6 +88,9 @@
     "resources:settings_sync",
     "resources:settings_voice_search",
     "resources:sync_and_google_services",
+    "resources:sync_and_google_services_sync_error",
+    "resources:sync_and_google_services_sync_off",
+    "resources:sync_and_google_services_sync_on",
     "//base",
     "//base:i18n",
     "//components/autofill/core/browser",
diff --git a/ios/chrome/browser/ui/settings/google_services/manage_sync_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/manage_sync_settings_mediator.mm
index 2a7f854..f1fecb2c 100644
--- a/ios/chrome/browser/ui/settings/google_services/manage_sync_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services/manage_sync_settings_mediator.mm
@@ -404,6 +404,12 @@
     switch (itemType) {
       case SyncEverythingItemType:
         self.syncSetupService->SetSyncingAllDataTypes(value);
+        if (value) {
+          // When sync everything is turned on, the autocomplete wallet
+          // should be turned on. This code can be removed once
+          // crbug.com/937234 is fixed.
+          self.autocompleteWalletPreference.value = true;
+        }
         break;
       case AutofillDataTypeItemType:
       case BookmarksDataTypeItemType:
diff --git a/ios/chrome/browser/ui/settings/resources/BUILD.gn b/ios/chrome/browser/ui/settings/resources/BUILD.gn
index 2a00f0e6..7024fe9 100644
--- a/ios/chrome/browser/ui/settings/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/resources/BUILD.gn
@@ -156,3 +156,27 @@
     "sync_and_google_services.imageset/sync_and_google_services@3x.png",
   ]
 }
+
+imageset("sync_and_google_services_sync_error") {
+  sources = [
+    "sync_and_google_services_sync_error.imageset/Contents.json",
+    "sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@2x.png",
+    "sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@3x.png",
+  ]
+}
+
+imageset("sync_and_google_services_sync_off") {
+  sources = [
+    "sync_and_google_services_sync_off.imageset/Contents.json",
+    "sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@2x.png",
+    "sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@3x.png",
+  ]
+}
+
+imageset("sync_and_google_services_sync_on") {
+  sources = [
+    "sync_and_google_services_sync_on.imageset/Contents.json",
+    "sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@2x.png",
+    "sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/Contents.json b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/Contents.json
new file mode 100644
index 0000000..71fa851
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "sync_and_google_services_sync_error@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "sync_and_google_services_sync_error@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@2x.png b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@2x.png
new file mode 100644
index 0000000..1e5940a
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@3x.png b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@3x.png
new file mode 100644
index 0000000..83e49263
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_error.imageset/sync_and_google_services_sync_error@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/Contents.json b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/Contents.json
new file mode 100644
index 0000000..106f625
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "sync_and_google_services_sync_off@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "sync_and_google_services_sync_off@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@2x.png b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@2x.png
new file mode 100644
index 0000000..b51d8cc2
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@3x.png b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@3x.png
new file mode 100644
index 0000000..beab667
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_off.imageset/sync_and_google_services_sync_off@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/Contents.json b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/Contents.json
new file mode 100644
index 0000000..3eff447
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+    "images": [
+        {
+            "idiom": "universal",
+            "scale": "2x",
+            "filename": "sync_and_google_services_sync_on@2x.png"
+        },
+        {
+            "idiom": "universal",
+            "scale": "3x",
+            "filename": "sync_and_google_services_sync_on@3x.png"
+        }
+    ],
+    "info": {
+        "version": 1,
+        "author": "xcode"
+    }
+}
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@2x.png b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@2x.png
new file mode 100644
index 0000000..385dee3e
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@3x.png b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@3x.png
new file mode 100644
index 0000000..c14dcbc0
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/resources/sync_and_google_services_sync_on.imageset/sync_and_google_services_sync_on@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 8200a3b..7d1e862 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -95,8 +95,12 @@
 const CGFloat kAccountProfilePhotoDimension = 40.0f;
 
 NSString* const kSyncAndGoogleServicesImageName = @"sync_and_google_services";
-NSString* const kSyncAndGoogleServicesErrorImageName =
-    @"google_services_sync_error";
+NSString* const kSyncAndGoogleServicesSyncErrorImageName =
+    @"sync_and_google_services_sync_error";
+NSString* const kSyncAndGoogleServicesSyncOffImageName =
+    @"sync_and_google_services_sync_off";
+NSString* const kSyncAndGoogleServicesSyncOnImageName =
+    @"sync_and_google_services_sync_on";
 NSString* const kSettingsSearchEngineImageName = @"settings_search_engine";
 NSString* const kSettingsPasswordsImageName = @"settings_passwords";
 NSString* const kSettingsAutofillCreditCardImageName =
@@ -1032,11 +1036,7 @@
 // Updates the Google services item to display the right icon and status message
 // in the detail text of the cell.
 - (void)updateGoogleServicesItem:(TableViewImageItem*)googleServicesItem {
-  // TODO(crbug.com/889470): Needs to include sync off, sync on, and error sync
-  // badges.
   googleServicesItem.detailTextColor = nil;
-  googleServicesItem.image =
-      [UIImage imageNamed:kSyncAndGoogleServicesImageName];
   SyncSetupService* syncSetupService =
       SyncSetupServiceFactory::GetForBrowserState(_browserState);
   AuthenticationService* authService =
@@ -1044,22 +1044,31 @@
   if (!authService->IsAuthenticated()) {
     // No sync status when the user is not signed-in.
     googleServicesItem.detailText = nil;
+    googleServicesItem.image =
+        [UIImage imageNamed:kSyncAndGoogleServicesImageName];
   } else if (!syncSetupService->HasFinishedInitialSetup()) {
     googleServicesItem.detailText =
         l10n_util::GetNSString(IDS_IOS_SYNC_SETUP_IN_PROGRESS);
+    googleServicesItem.image =
+        [UIImage imageNamed:kSyncAndGoogleServicesSyncOnImageName];
   } else if (!IsTransientSyncError(syncSetupService->GetSyncServiceState())) {
     googleServicesItem.detailTextColor = UIColor.redColor;
     googleServicesItem.detailText =
         GetSyncErrorDescriptionForSyncSetupService(syncSetupService);
     googleServicesItem.image =
-        [UIImage imageNamed:kSyncAndGoogleServicesErrorImageName];
+        [UIImage imageNamed:kSyncAndGoogleServicesSyncErrorImageName];
   } else if (syncSetupService->IsSyncEnabled()) {
     googleServicesItem.detailText =
         l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNC_ON);
+    googleServicesItem.image =
+        [UIImage imageNamed:kSyncAndGoogleServicesSyncOnImageName];
   } else {
     googleServicesItem.detailText =
         l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SYNC_OFF);
+    googleServicesItem.image =
+        [UIImage imageNamed:kSyncAndGoogleServicesSyncOffImageName];
   }
+  DCHECK(googleServicesItem.image);
 }
 
 // Updates and reloads the Google service cell.
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
index 3986051..89a26ea9 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_image_item.mm
@@ -79,6 +79,7 @@
               reuseIdentifier:(NSString*)reuseIdentifier {
   self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
   if (self) {
+    self.isAccessibilityElement = YES;
     _imageView = [[UIImageView alloc] init];
     // The favicon image is smaller than its UIImageView's bounds, so center it.
     _imageView.contentMode = UIViewContentModeCenter;
@@ -180,4 +181,14 @@
   }
 }
 
+#pragma mark - UIAccessibility
+
+- (NSString*)accessibilityLabel {
+  if (self.detailTextLabel.text) {
+    return [NSString stringWithFormat:@"%@, %@", self.titleLabel.text,
+                                      self.detailTextLabel.text];
+  }
+  return self.titleLabel.text;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
index b9bd780..cc88c196 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_coordinator.mm
@@ -170,6 +170,12 @@
   self.enableAnimationsForOmniboxFocus = NO;
   [self fakeboxFocused];
   self.enableAnimationsForOmniboxFocus = YES;
+  // If the pasteboard is containing a URL, the omnibox popup suggestions are
+  // displayed as soon as the omnibox is focused.
+  // If the fake omnibox animation is triggered at the same time, it is possible
+  // to see the NTP going up where the real omnibox should be displayed.
+  if ([self.locationBarCoordinator omniboxPopupHasAutocompleteResults])
+    [self onFakeboxAnimationComplete];
 }
 
 - (void)fakeboxFocused {
diff --git a/ios/chrome/browser/ui/util/dynamic_type_util.h b/ios/chrome/browser/ui/util/dynamic_type_util.h
index c3f55f6..333bf41 100644
--- a/ios/chrome/browser/ui/util/dynamic_type_util.h
+++ b/ios/chrome/browser/ui/util/dynamic_type_util.h
@@ -22,4 +22,11 @@
                                         UIContentSizeCategory min_category,
                                         UIContentSizeCategory max_category);
 
+// Returns an UIFont* calculated by |style| and
+// min(|currentCategory|,|maxCategory|).
+UIFont* PreferredFontForTextStyleWithMaxCategory(
+    UIFontTextStyle style,
+    UIContentSizeCategory currentCategory,
+    UIContentSizeCategory maxCategory);
+
 #endif  // IOS_CHROME_BROWSER_UI_UTIL_DYNAMIC_TYPE_UTIL_H_
diff --git a/ios/chrome/browser/ui/util/dynamic_type_util.mm b/ios/chrome/browser/ui/util/dynamic_type_util.mm
index 71b5b331..0cda4e79 100644
--- a/ios/chrome/browser/ui/util/dynamic_type_util.mm
+++ b/ios/chrome/browser/ui/util/dynamic_type_util.mm
@@ -57,3 +57,17 @@
       max_multiplier,
       std::max(min_multiplier, SystemSuggestedFontSizeMultiplier(category)));
 }
+
+UIFont* PreferredFontForTextStyleWithMaxCategory(
+    UIFontTextStyle style,
+    UIContentSizeCategory currentCategory,
+    UIContentSizeCategory maxCategory) {
+  NSComparisonResult result =
+      UIContentSizeCategoryCompareToCategory(currentCategory, maxCategory);
+  UIContentSizeCategory category =
+      result == NSOrderedDescending ? maxCategory : currentCategory;
+  return [UIFont preferredFontForTextStyle:style
+             compatibleWithTraitCollection:
+                 [UITraitCollection
+                     traitCollectionWithPreferredContentSizeCategory:category]];
+}
diff --git a/ios/chrome/browser/ui/util/dynamic_type_util_unittest.mm b/ios/chrome/browser/ui/util/dynamic_type_util_unittest.mm
index fb2e783..34cffb73 100644
--- a/ios/chrome/browser/ui/util/dynamic_type_util_unittest.mm
+++ b/ios/chrome/browser/ui/util/dynamic_type_util_unittest.mm
@@ -6,6 +6,7 @@
 
 #import <UIKit/UIKit.h>
 
+#include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "third_party/ocmock/gtest_support.h"
@@ -16,85 +17,66 @@
 
 // Test fixture for DynamicTypeUtil class.
 class DynamicTypeUtilTest : public PlatformTest {
- public:
+ protected:
   DynamicTypeUtilTest() {}
   ~DynamicTypeUtilTest() override {}
 
-  void SetPreferredContentSizeCategory(UILabel* testLabel,
-                                       UIViewController* viewController,
-                                       UIContentSizeCategory category) {
-    UITraitCollection* overrideTraitCollection = [UITraitCollection
-        traitCollectionWithPreferredContentSizeCategory:category];
-    [viewController.parentViewController
-        setOverrideTraitCollection:overrideTraitCollection
-            forChildViewController:viewController];
-    [testLabel removeFromSuperview];
-    [viewController.view addSubview:testLabel];
+  UIFont* PreferredFontForTextStyleAndSizeCategory(
+      UIFontTextStyle style,
+      UIContentSizeCategory category) {
+    return
+        [UIFont preferredFontForTextStyle:style
+            compatibleWithTraitCollection:
+                [UITraitCollection
+                    traitCollectionWithPreferredContentSizeCategory:category]];
   }
 };
 
 // Checks that the font sizes associated with the "body"
 // preferredContentSizeCategory aren't changing in new iOS releases.
 TEST_F(DynamicTypeUtilTest, TestFontSize) {
-  UIViewController* parentViewController = [[UIViewController alloc] init];
-  UIViewController* viewController = [[UIViewController alloc] init];
-  [parentViewController addChildViewController:viewController];
-  [parentViewController.view addSubview:viewController.view];
-  [viewController didMoveToParentViewController:parentViewController];
-
-  UILabel* testLabel = [[UILabel alloc] init];
-  testLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
-  testLabel.adjustsFontForContentSizeCategory = YES;
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryExtraSmall);
-  EXPECT_EQ(14.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategorySmall);
-  EXPECT_EQ(15.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryMedium);
-  EXPECT_EQ(16.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryLarge);
-  EXPECT_EQ(17.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryExtraLarge);
-  EXPECT_EQ(19.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryExtraExtraLarge);
-  EXPECT_EQ(21.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryExtraExtraExtraLarge);
-  EXPECT_EQ(23.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryAccessibilityMedium);
-  EXPECT_EQ(28.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryAccessibilityLarge);
-  EXPECT_EQ(33.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(testLabel, viewController,
-                                  UIContentSizeCategoryAccessibilityExtraLarge);
-  EXPECT_EQ(40.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(
-      testLabel, viewController,
-      UIContentSizeCategoryAccessibilityExtraExtraLarge);
-  EXPECT_EQ(47.f, testLabel.font.pointSize);
-
-  SetPreferredContentSizeCategory(
-      testLabel, viewController,
-      UIContentSizeCategoryAccessibilityExtraExtraExtraLarge);
-  EXPECT_EQ(53.f, testLabel.font.pointSize);
+  EXPECT_EQ(14.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody, UIContentSizeCategoryExtraSmall)
+                      .pointSize);
+  EXPECT_EQ(15.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody, UIContentSizeCategorySmall)
+                      .pointSize);
+  EXPECT_EQ(16.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody, UIContentSizeCategoryMedium)
+                      .pointSize);
+  EXPECT_EQ(17.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody, UIContentSizeCategoryLarge)
+                      .pointSize);
+  EXPECT_EQ(19.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody, UIContentSizeCategoryExtraLarge)
+                      .pointSize);
+  EXPECT_EQ(21.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody, UIContentSizeCategoryExtraExtraLarge)
+                      .pointSize);
+  EXPECT_EQ(23.f,
+            PreferredFontForTextStyleAndSizeCategory(
+                UIFontTextStyleBody, UIContentSizeCategoryExtraExtraExtraLarge)
+                .pointSize);
+  EXPECT_EQ(28.f,
+            PreferredFontForTextStyleAndSizeCategory(
+                UIFontTextStyleBody, UIContentSizeCategoryAccessibilityMedium)
+                .pointSize);
+  EXPECT_EQ(33.f,
+            PreferredFontForTextStyleAndSizeCategory(
+                UIFontTextStyleBody, UIContentSizeCategoryAccessibilityLarge)
+                .pointSize);
+  EXPECT_EQ(40.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody,
+                      UIContentSizeCategoryAccessibilityExtraLarge)
+                      .pointSize);
+  EXPECT_EQ(47.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody,
+                      UIContentSizeCategoryAccessibilityExtraExtraLarge)
+                      .pointSize);
+  EXPECT_EQ(53.f, PreferredFontForTextStyleAndSizeCategory(
+                      UIFontTextStyleBody,
+                      UIContentSizeCategoryAccessibilityExtraExtraExtraLarge)
+                      .pointSize);
 }
 
 // Tests that the clamped version of the font size multipler is working.
@@ -136,3 +118,58 @@
       SystemSuggestedFontSizeMultiplier(UIContentSizeCategoryExtraExtraLarge),
       multiplier);
 }
+
+// Tests that |PreferredFontForTextStyleWithMaxCategory| works well with various
+// input scenarios.
+TEST_F(DynamicTypeUtilTest, PreferredFontSize) {
+  // Use normal category as maxmium category.
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryExtraSmall),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryExtraSmall,
+                  UIContentSizeCategoryMedium));
+
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryMedium),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryMedium,
+                  UIContentSizeCategoryMedium));
+
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryMedium),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryExtraExtraLarge,
+                  UIContentSizeCategoryMedium));
+
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryMedium),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryAccessibilityLarge,
+                  UIContentSizeCategoryMedium));
+
+  // Use accessibility category as maxmium category.
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryExtraSmall),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryExtraSmall,
+                  UIContentSizeCategoryAccessibilityLarge));
+
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryMedium),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryMedium,
+                  UIContentSizeCategoryAccessibilityLarge));
+
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryAccessibilityLarge),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryAccessibilityLarge,
+                  UIContentSizeCategoryAccessibilityLarge));
+
+  EXPECT_NSEQ(PreferredFontForTextStyleAndSizeCategory(
+                  UIFontTextStyleBody, UIContentSizeCategoryAccessibilityLarge),
+              PreferredFontForTextStyleWithMaxCategory(
+                  UIFontTextStyleBody,
+                  UIContentSizeCategoryAccessibilityExtraExtraLarge,
+                  UIContentSizeCategoryAccessibilityLarge));
+}
diff --git a/ios/chrome/browser/ui/util/uikit_ui_util.h b/ios/chrome/browser/ui/util/uikit_ui_util.h
index b7ac59bf..ac3b342 100644
--- a/ios/chrome/browser/ui/util/uikit_ui_util.h
+++ b/ios/chrome/browser/ui/util/uikit_ui_util.h
@@ -255,9 +255,4 @@
 // more than 99 tabs open.
 NSString* TextForTabCount(long count);
 
-UIFont* PreferredFontForTextStyleWithMaxCategory(
-    UIFontTextStyle style,
-    UIContentSizeCategory currentCategory,
-    UIContentSizeCategory maxCategory);
-
 #endif  // IOS_CHROME_BROWSER_UI_UTIL_UIKIT_UI_UTIL_H_
diff --git a/ios/chrome/browser/ui/util/uikit_ui_util.mm b/ios/chrome/browser/ui/util/uikit_ui_util.mm
index 96d1b1b..645cd1f 100644
--- a/ios/chrome/browser/ui/util/uikit_ui_util.mm
+++ b/ios/chrome/browser/ui/util/uikit_ui_util.mm
@@ -690,20 +690,3 @@
     return @":)";
   return [NSString stringWithFormat:@"%ld", count];
 }
-
-UIFont* PreferredFontForTextStyleWithMaxCategory(
-    UIFontTextStyle style,
-    UIContentSizeCategory currentCategory,
-    UIContentSizeCategory maxCategory) {
-  CGFloat maxMultiplier = SystemSuggestedFontSizeMultiplier(maxCategory);
-  CGFloat currentMultiplier =
-      SystemSuggestedFontSizeMultiplier(currentCategory);
-  if (currentMultiplier > maxMultiplier) {
-    return [UIFont
-            preferredFontForTextStyle:style
-        compatibleWithTraitCollection:
-            [UITraitCollection
-                traitCollectionWithPreferredContentSizeCategory:maxCategory]];
-  }
-  return [UIFont preferredFontForTextStyle:style];
-}
diff --git a/ios/chrome/browser/web_resource/web_resource_util.cc b/ios/chrome/browser/web_resource/web_resource_util.cc
index 349c75a..65c7d1c 100644
--- a/ios/chrome/browser/web_resource/web_resource_util.cc
+++ b/ios/chrome/browser/web_resource/web_resource_util.cc
@@ -23,14 +23,6 @@
 const char kUnexpectedJSONFormatError[] =
     "Data from web resource server does not have expected format.";
 
-// Runs |error_callback| with |error| on |task_runner|.
-void PostErrorTask(base::TaskRunner* task_runner,
-                   const WebResourceService::ErrorCallback& error_callback,
-                   const char error[]) {
-  task_runner->PostTask(FROM_HERE,
-                        base::BindOnce(error_callback, std::string(error)));
-}
-
 // Parses |data| as a JSON string and calls back on |task_runner|.
 // Must not be called on the UI thread, for performance reasons.
 void ParseJSONOnBackgroundThread(
@@ -39,24 +31,29 @@
     const WebResourceService::SuccessCallback& success_callback,
     const WebResourceService::ErrorCallback& error_callback) {
   if (data.empty()) {
-    PostErrorTask(task_runner, error_callback, kInvalidDataTypeError);
+    task_runner->PostTask(
+        FROM_HERE, base::BindOnce(error_callback, kInvalidDataTypeError));
     return;
   }
 
-  std::unique_ptr<base::Value> value(base::JSONReader::ReadDeprecated(data));
-  if (!value.get()) {
+  base::Optional<base::Value> value(base::JSONReader::Read(data));
+  if (!value) {
     // Page information not properly read, or corrupted.
-    PostErrorTask(task_runner, error_callback, kInvalidDataTypeError);
+    task_runner->PostTask(
+        FROM_HERE, base::BindOnce(error_callback, kInvalidDataTypeError));
     return;
   }
 
   if (!value->is_dict()) {
-    PostErrorTask(task_runner, error_callback, kUnexpectedJSONFormatError);
+    task_runner->PostTask(
+        FROM_HERE, base::BindOnce(error_callback, kUnexpectedJSONFormatError));
     return;
   }
 
-  task_runner->PostTask(FROM_HERE,
-                        base::BindOnce(success_callback, std::move(value)));
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(success_callback,
+                     base::Value::ToUniquePtrValue(std::move(value).value())));
 }
 
 // Starts the parsing of |data| as a JSON string asynchronously on a background
diff --git a/ios/chrome/search_widget_extension/BUILD.gn b/ios/chrome/search_widget_extension/BUILD.gn
index 946289ae..cb65f7d 100644
--- a/ios/chrome/search_widget_extension/BUILD.gn
+++ b/ios/chrome/search_widget_extension/BUILD.gn
@@ -50,12 +50,12 @@
     "copied_content_view.mm",
     "search_action_view.h",
     "search_action_view.mm",
+    "search_widget_constants.h",
+    "search_widget_constants.mm",
     "search_widget_view.h",
     "search_widget_view.mm",
     "search_widget_view_controller.h",
     "search_widget_view_controller.mm",
-    "ui_util.h",
-    "ui_util.mm",
   ]
 
   deps = [
diff --git a/ios/chrome/search_widget_extension/copied_content_view.mm b/ios/chrome/search_widget_extension/copied_content_view.mm
index a8630c5..59b221e 100644
--- a/ios/chrome/search_widget_extension/copied_content_view.mm
+++ b/ios/chrome/search_widget_extension/copied_content_view.mm
@@ -7,7 +7,8 @@
 #import <NotificationCenter/NotificationCenter.h>
 
 #include "base/logging.h"
-#import "ios/chrome/search_widget_extension/ui_util.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
+#import "ios/chrome/search_widget_extension/search_widget_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -41,12 +42,6 @@
 
 @implementation CopiedContentView
 
-@synthesize copiedContentLabel = _copiedContentLabel;
-@synthesize copiedText = _copiedText;
-@synthesize openCopiedContentTitleLabel = _openCopiedContentTitleLabel;
-@synthesize hairlineView = _hairlineView;
-@synthesize copiedButtonView = _copiedButtonView;
-
 - (instancetype)initWithActionTarget:(id)target
                       actionSelector:(SEL)actionSelector {
   DCHECK(target);
@@ -71,8 +66,7 @@
          @[ primaryEffectView, secondaryEffectView ]) {
       [self addSubview:effectView];
       effectView.translatesAutoresizingMaskIntoConstraints = NO;
-      [NSLayoutConstraint
-          activateConstraints:ui_util::CreateSameConstraints(self, effectView)];
+      AddSameConstraints(self, effectView);
       effectView.userInteractionEnabled = NO;
     }
 
@@ -109,26 +103,24 @@
 
       [_copiedButtonView.leadingAnchor
           constraintEqualToAnchor:self.leadingAnchor
-                         constant:ui_util::kContentMargin],
+                         constant:kContentMargin],
       [_copiedButtonView.trailingAnchor
           constraintEqualToAnchor:self.trailingAnchor
-                         constant:-ui_util::kContentMargin],
-      [_copiedButtonView.topAnchor
-          constraintEqualToAnchor:self.topAnchor
-                         constant:ui_util::kContentMargin],
-      [_copiedButtonView.bottomAnchor
-          constraintEqualToAnchor:self.bottomAnchor
-                         constant:-ui_util::kContentMargin],
+                         constant:-kContentMargin],
+      [_copiedButtonView.topAnchor constraintEqualToAnchor:self.topAnchor
+                                                  constant:kContentMargin],
+      [_copiedButtonView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor
+                                                     constant:-kContentMargin],
 
       [_openCopiedContentTitleLabel.topAnchor
           constraintEqualToAnchor:_copiedButtonView.topAnchor
                          constant:kURLButtonMargin],
       [_openCopiedContentTitleLabel.leadingAnchor
           constraintEqualToAnchor:_copiedButtonView.leadingAnchor
-                         constant:ui_util::kContentMargin],
+                         constant:kContentMargin],
       [_openCopiedContentTitleLabel.trailingAnchor
           constraintEqualToAnchor:_copiedButtonView.trailingAnchor
-                         constant:-ui_util::kContentMargin],
+                         constant:-kContentMargin],
 
       [_copiedContentLabel.topAnchor
           constraintEqualToAnchor:_openCopiedContentTitleLabel.bottomAnchor],
diff --git a/ios/chrome/search_widget_extension/search_action_view.mm b/ios/chrome/search_widget_extension/search_action_view.mm
index 2732b7431..e5672dc0 100644
--- a/ios/chrome/search_widget_extension/search_action_view.mm
+++ b/ios/chrome/search_widget_extension/search_action_view.mm
@@ -7,7 +7,8 @@
 #import <NotificationCenter/NotificationCenter.h>
 
 #include "base/logging.h"
-#import "ios/chrome/search_widget_extension/ui_util.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
+#import "ios/chrome/search_widget_extension/search_widget_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -45,8 +46,7 @@
       [self addSubview:effectView];
       effectView.translatesAutoresizingMaskIntoConstraints = NO;
       effectView.userInteractionEnabled = NO;
-      [NSLayoutConstraint
-          activateConstraints:ui_util::CreateSameConstraints(self, effectView)];
+      AddSameConstraints(self, effectView);
     }
 
     UIView* circleView = [[UIView alloc] initWithFrame:CGRectZero];
@@ -66,12 +66,11 @@
     UIStackView* stack = [[UIStackView alloc]
         initWithArrangedSubviews:@[ circleView, labelView ]];
     stack.axis = UILayoutConstraintAxisVertical;
-    stack.spacing = ui_util::kIconSpacing;
+    stack.spacing = kIconSpacing;
     stack.alignment = UIStackViewAlignmentCenter;
     stack.translatesAutoresizingMaskIntoConstraints = NO;
     [secondaryEffectView.contentView addSubview:stack];
-    [NSLayoutConstraint activateConstraints:ui_util::CreateSameConstraints(
-                                                secondaryEffectView, stack)];
+    AddSameConstraints(secondaryEffectView, stack);
     UIImage* iconImage = [UIImage imageNamed:imageName];
     iconImage =
         [iconImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
diff --git a/ios/chrome/search_widget_extension/search_widget_constants.h b/ios/chrome/search_widget_extension/search_widget_constants.h
new file mode 100644
index 0000000..44db73f0
--- /dev/null
+++ b/ios/chrome/search_widget_extension/search_widget_constants.h
@@ -0,0 +1,14 @@
+// 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_SEARCH_WIDGET_EXTENSION_SEARCH_WIDGET_CONSTANTS_H_
+#define IOS_CHROME_SEARCH_WIDGET_EXTENSION_SEARCH_WIDGET_CONSTANTS_H_
+
+// The spacing to use between action icons.
+extern CGFloat const kIconSpacing;
+
+// The spacing between content and edges.
+extern CGFloat const kContentMargin;
+
+#endif  // IOS_CHROME_SEARCH_WIDGET_EXTENSION_SEARCH_WIDGET_CONSTANTS_H_
diff --git a/ios/chrome/search_widget_extension/search_widget_constants.mm b/ios/chrome/search_widget_extension/search_widget_constants.mm
new file mode 100644
index 0000000..26f301b
--- /dev/null
+++ b/ios/chrome/search_widget_extension/search_widget_constants.mm
@@ -0,0 +1,14 @@
+// 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 <UIKit/UIKit.h>
+
+#include "ios/chrome/search_widget_extension/search_widget_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+CGFloat const kIconSpacing = 5;
+CGFloat const kContentMargin = 12;
diff --git a/ios/chrome/search_widget_extension/search_widget_view.mm b/ios/chrome/search_widget_extension/search_widget_view.mm
index ca8c427..f6ad453 100644
--- a/ios/chrome/search_widget_extension/search_widget_view.mm
+++ b/ios/chrome/search_widget_extension/search_widget_view.mm
@@ -6,7 +6,7 @@
 #include "base/logging.h"
 #import "ios/chrome/search_widget_extension/copied_content_view.h"
 #import "ios/chrome/search_widget_extension/search_action_view.h"
-#import "ios/chrome/search_widget_extension/ui_util.h"
+#import "ios/chrome/search_widget_extension/search_widget_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -124,7 +124,7 @@
       [self.actionsContent
           systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
           .height;
-  return height + 2 * ui_util::kContentMargin;
+  return height + 2 * kContentMargin;
 }
 
 - (CGFloat)copiedURLSectionHeight {
@@ -169,7 +169,7 @@
   actionsContentStack.axis = UILayoutConstraintAxisHorizontal;
   actionsContentStack.alignment = UIStackViewAlignmentTop;
   actionsContentStack.distribution = UIStackViewDistributionFillEqually;
-  actionsContentStack.spacing = ui_util::kIconSpacing;
+  actionsContentStack.spacing = kIconSpacing;
   actionsContentStack.layoutMargins = UIEdgeInsetsZero;
   actionsContentStack.layoutMarginsRelativeArrangement = YES;
   actionsContentStack.translatesAutoresizingMaskIntoConstraints = NO;
@@ -186,12 +186,12 @@
   NSLayoutConstraint* actionsLeadingConstraint =
       [self.actionsContent.leadingAnchor
           constraintEqualToAnchor:self.actionsSection.leadingAnchor
-                         constant:ui_util::kContentMargin];
+                         constant:kContentMargin];
   actionsLeadingConstraint.priority = UILayoutPriorityDefaultHigh;
   NSLayoutConstraint* actionsTrailingConstraint =
       [self.actionsContent.trailingAnchor
           constraintEqualToAnchor:self.actionsSection.trailingAnchor
-                         constant:-ui_util::kContentMargin];
+                         constant:-kContentMargin];
   actionsTrailingConstraint.priority = UILayoutPriorityDefaultHigh;
 
   [NSLayoutConstraint activateConstraints:@[
diff --git a/ios/chrome/search_widget_extension/search_widget_view_controller.mm b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
index 9e080ed..548eaf3 100644
--- a/ios/chrome/search_widget_extension/search_widget_view_controller.mm
+++ b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
@@ -39,42 +39,10 @@
 @property(nonatomic, assign) BOOL supportsSearchByImage;
 @property(nonatomic, readonly) BOOL copiedContentBehaviorEnabled;
 
-// Updates the widget with latest data from the clipboard. Returns whether any
-// visual updates occurred.
-- (BOOL)updateWidget;
-// Opens the main application with the given |command|.
-- (void)openAppWithCommand:(NSString*)command;
-// Opens the main application with the given |command|, |text|, and |image|.
-- (void)openAppWithCommand:(NSString*)command
-                      text:(NSString*)text
-                 imageData:(NSData*)imageData;
-// Returns the dictionary of commands to pass via user defaults to open the main
-// application for a given |command| and optional |text| and |image|.
-+ (NSDictionary*)dictForCommand:(NSString*)command
-                           text:(NSString*)text
-                      imageData:(NSData*)imageData;
-// Register a display of the widget in the app_group NSUserDefaults.
-// Metrics on the widget usage will be sent (if enabled) on the next Chrome
-// startup.
-- (void)registerWidgetDisplay;
-// Sets the copied content type. |copiedText| should be provided if the content
-// type requires textual data, otherwise it should be nil. Likewise,
-// |copiedImage| should be provided if the content type requires image data.
-// Also saves the data and returns YES if the screen needs updating and NO
-// otherwise.
-- (BOOL)setCopiedContentType:(CopiedContentType)type
-                  copiedText:(NSString*)copiedText
-                 copiedImage:(UIImage*)copiedImage;
-
 @end
 
 @implementation SearchWidgetViewController
 
-@synthesize widgetView = _widgetView;
-@synthesize copiedText = _copiedText;
-@synthesize copiedContentType = _copiedContentType;
-@synthesize clipboardRecentContent = _clipboardRecentContent;
-
 - (instancetype)init {
   self = [super init];
   if (self) {
@@ -132,6 +100,9 @@
   completionHandler([self updateWidget] ? NCUpdateResultNewData
                                         : NCUpdateResultNoData);
 }
+
+// Updates the widget with latest data from the clipboard. Returns whether any
+// visual updates occurred.
 - (BOOL)updateWidget {
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
   NSString* fieldTrialKey =
@@ -271,10 +242,14 @@
 
 #pragma mark - internal
 
+// Opens the main application with the given |command|.
 - (void)openAppWithCommand:(NSString*)command {
   return [self openAppWithCommand:command text:nil imageData:nil];
 }
 
+// Register a display of the widget in the app_group NSUserDefaults.
+// Metrics on the widget usage will be sent (if enabled) on the next Chrome
+// startup.
 - (void)registerWidgetDisplay {
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
   NSInteger numberOfDisplay =
@@ -283,6 +258,7 @@
                       forKey:app_group::kSearchExtensionDisplayCount];
 }
 
+// Opens the main application with the given |command|, |text|, and |image|.
 - (void)openAppWithCommand:(NSString*)command
                       text:(NSString*)text
                  imageData:(NSData*)imageData {
@@ -312,6 +288,8 @@
   [self.extensionContext openURL:openURL completionHandler:nil];
 }
 
+// Returns the dictionary of commands to pass via user defaults to open the main
+// application for a given |command| and optional |text| and |image|.
 + (NSDictionary*)dictForCommand:(NSString*)command
                            text:(NSString*)text
                       imageData:(NSData*)imageData {
@@ -343,6 +321,11 @@
   return baseKeys;
 }
 
+// Sets the copied content type. |copiedText| should be provided if the content
+// type requires textual data, otherwise it should be nil. Likewise,
+// |copiedImage| should be provided if the content type requires image data.
+// Also saves the data and returns YES if the screen needs updating and NO
+// otherwise.
 - (BOOL)setCopiedContentType:(CopiedContentType)type
                   copiedText:(NSString*)copiedText
                  copiedImage:(UIImage*)copiedImage {
diff --git a/ios/chrome/search_widget_extension/ui_util.h b/ios/chrome/search_widget_extension/ui_util.h
deleted file mode 100644
index 9897552..0000000
--- a/ios/chrome/search_widget_extension/ui_util.h
+++ /dev/null
@@ -1,23 +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 IOS_CHROME_SEARCH_WIDGET_EXTENSION_UI_UTIL_H_
-#define IOS_CHROME_SEARCH_WIDGET_EXTENSION_UI_UTIL_H_
-
-namespace ui_util {
-
-// The spacing to use between action icons.
-extern CGFloat const kIconSpacing;
-
-// The spacing between content and edges.
-extern CGFloat const kContentMargin;
-
-// Returns constraints to make two views' size and center equal by pinning
-// leading, trailing, top and bottom anchors.
-NSArray<NSLayoutConstraint*>* CreateSameConstraints(UIView* view1,
-                                                    UIView* view2);
-
-}  // namespace ui_util
-
-#endif  // IOS_CHROME_SEARCH_WIDGET_EXTENSION_UI_UTIL_H_
diff --git a/ios/chrome/search_widget_extension/ui_util.mm b/ios/chrome/search_widget_extension/ui_util.mm
deleted file mode 100644
index 1b4dae7..0000000
--- a/ios/chrome/search_widget_extension/ui_util.mm
+++ /dev/null
@@ -1,28 +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.
-
-#import <UIKit/UIKit.h>
-
-#include "ios/chrome/search_widget_extension/ui_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace ui_util {
-
-CGFloat const kIconSpacing = 5;
-CGFloat const kContentMargin = 12;
-
-NSArray<NSLayoutConstraint*>* CreateSameConstraints(UIView* view1,
-                                                    UIView* view2) {
-  return @[
-    [view1.leadingAnchor constraintEqualToAnchor:view2.leadingAnchor],
-    [view1.trailingAnchor constraintEqualToAnchor:view2.trailingAnchor],
-    [view1.topAnchor constraintEqualToAnchor:view2.topAnchor],
-    [view1.bottomAnchor constraintEqualToAnchor:view2.bottomAnchor]
-  ];
-}
-
-}  // namespace ui_util
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 98c2e32a..883988a8 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -213,6 +213,7 @@
     "//ios/chrome/browser/ui/main:unit_tests",
     "//ios/chrome/browser/ui/main_content:unit_tests",
     "//ios/chrome/browser/ui/ntp:unit_tests",
+    "//ios/chrome/browser/ui/ntp_tile_views:unit_tests",
     "//ios/chrome/browser/ui/omnibox:unit_tests",
     "//ios/chrome/browser/ui/omnibox/popup:unit_tests",
     "//ios/chrome/browser/ui/payments:unit_tests",
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 93a8c08..97b6437a 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -10,7 +10,6 @@
     ":ios_chrome_autofill_automation_egtests",
     ":ios_chrome_bookmarks_egtests",
     ":ios_chrome_device_check_egtests",
-    ":ios_chrome_external_url_egtests",
     ":ios_chrome_flaky_egtests",
     ":ios_chrome_integration_egtests",
     ":ios_chrome_manual_fill_egtests",
@@ -20,6 +19,7 @@
     ":ios_chrome_settings_egtests",
     ":ios_chrome_smoke_egtests",
     ":ios_chrome_tab_grid_egtests",
+    ":ios_chrome_translate_egtests",
     ":ios_chrome_ui_egtests",
     ":ios_chrome_unified_consent_egtests",
     ":ios_chrome_web_egtests",
@@ -195,9 +195,9 @@
   libs = [ "XCTest.framework" ]
 }
 
-chrome_ios_eg_test("ios_chrome_external_url_egtests") {
+chrome_ios_eg_test("ios_chrome_translate_egtests") {
   deps = [
-    "//ios/chrome/browser/translate:external_url_eg_tests",
+    "//ios/chrome/browser/translate:eg_tests",
   ]
 }
 
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 7c0c060e..3e5db15 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -176,6 +176,7 @@
     "//ios/testing:http_server_bundle_data",
     "//ios/web/browsing_data:browsing_data_unittests",
     "//ios/web/download:download_unittests",
+    "//ios/web/find_in_page:find_in_page_unittests",
     "//ios/web/interstitials:interstitials_unittests",
   ]
 
@@ -379,6 +380,7 @@
     "//ios/net",
     "//ios/testing:ocmock_support",
     "//ios/web",
+    "//ios/web/find_in_page",
     "//ios/web/interstitials",
     "//ios/web/navigation",
     "//ios/web/navigation:wk_navigation_util",
diff --git a/ios/web/find_in_page/BUILD.gn b/ios/web/find_in_page/BUILD.gn
index a876b97..a0721c2c 100644
--- a/ios/web/find_in_page/BUILD.gn
+++ b/ios/web/find_in_page/BUILD.gn
@@ -20,3 +20,22 @@
 
   configs += [ "//build/config/compiler:enable_arc" ]
 }
+
+source_set("find_in_page_unittests") {
+  testonly = true
+  deps = [
+    ":find_in_page",
+    "//base",
+    "//base/test:test_support",
+    "//ios/web/public",
+    "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
+    "//testing/gtest",
+  ]
+
+  sources = [
+    "find_in_page_manger_impl_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
new file mode 100644
index 0000000..c625b2a4
--- /dev/null
+++ b/ios/web/find_in_page/find_in_page_manger_impl_unittest.mm
@@ -0,0 +1,189 @@
+// 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/find_in_page/find_in_page_manager_impl.h"
+
+#include "base/run_loop.h"
+#import "base/test/ios/wait_util.h"
+#include "base/values.h"
+#import "ios/web/find_in_page/find_in_page_constants.h"
+#import "ios/web/public/test/fakes/fake_find_in_page_manager_delegate.h"
+#include "ios/web/public/test/fakes/fake_web_frame.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "ios/web/public/test/web_test.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using base::test::ios::kWaitForJSCompletionTimeout;
+using base::test::ios::WaitUntilConditionOrTimeout;
+
+namespace {
+
+// Frame ids of fake web frames used in this test class.
+const char kOneMatchFrameId[] = "frame_with_one_match";
+const char kTwoMatchesFrameId[] = "frame_with_two_matches";
+
+}  // namespace
+
+namespace web {
+
+// Tests FindInPageManagerImpl and verifies that the state of
+// FindInPageManagerDelegate is correct depending on what web frames return.
+class FindInPageManagerImplTest : public WebTest {
+ protected:
+  FindInPageManagerImplTest()
+      : test_web_state_(std::make_unique<TestWebState>()) {
+    test_web_state_->CreateWebFramesManager();
+    FindInPageManagerImpl::CreateForWebState(test_web_state_.get());
+    GetFindInPageManager()->SetDelegate(&fake_delegate_);
+  }
+
+  // Returns the FindInPageManager associated with |test_web_state_|.
+  FindInPageManager* GetFindInPageManager() {
+    return FindInPageManager::FromWebState(test_web_state_.get());
+  }
+  // Returns a FakeWebFrame with id |frame_id| that will return |js_result| for
+  // the function call "findInString.findString".
+  std::unique_ptr<FakeWebFrame> CreateWebFrameWithJsResultForFind(
+      std::unique_ptr<base::Value> js_result,
+      const std::string& frame_id) {
+    auto frame_with_one_match =
+        std::make_unique<FakeWebFrame>(frame_id,
+                                       /*is_main_frame=*/false, GURL());
+    frame_with_one_match->AddJsResultForFunctionCall(std::move(js_result),
+                                                     kFindInPageSearch);
+    return frame_with_one_match;
+  }
+
+  std::unique_ptr<TestWebState> test_web_state_;
+  FakeFindInPageManagerDelegate fake_delegate_;
+};
+
+// Tests that Find In Page responds with a total match count of three when a
+// frame has one match and another frame has two matches.
+TEST_F(FindInPageManagerImplTest, FindMatchesMultipleFrames) {
+  auto frame_with_one_match = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(1.0), kOneMatchFrameId);
+  FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
+  auto frame_with_two_matches = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(2.0), kTwoMatchesFrameId);
+  FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
+  test_web_state_->AddWebFrame(std::move(frame_with_one_match));
+  test_web_state_->AddWebFrame(std::move(frame_with_two_matches));
+
+  GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
+
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_one_match_ptr->last_javascript_call());
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_two_matches_ptr->last_javascript_call());
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
+    base::RunLoop().RunUntilIdle();
+    return fake_delegate_.state();
+  }));
+  EXPECT_EQ(3, fake_delegate_.state()->match_count);
+}
+
+// Tests that Find In Page responds with a total match count of one when a frame
+// has one match but find in one frame was cancelled. This can occur if the
+// frame becomes unavailable.
+TEST_F(FindInPageManagerImplTest, FrameCancelFind) {
+  auto frame_with_null_result = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(), "frame");
+  FakeWebFrame* frame_with_null_result_ptr = frame_with_null_result.get();
+  auto frame_with_one_match = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(1.0), kOneMatchFrameId);
+  FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
+  test_web_state_->AddWebFrame(std::move(frame_with_null_result));
+  test_web_state_->AddWebFrame(std::move(frame_with_one_match));
+
+  GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
+
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_null_result_ptr->last_javascript_call());
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_one_match_ptr->last_javascript_call());
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
+    base::RunLoop().RunUntilIdle();
+    return fake_delegate_.state();
+  }));
+  EXPECT_EQ(1, fake_delegate_.state()->match_count);
+}
+
+// Tests that Find in Page returns a total match count matching the latest find
+// if two finds are called.
+TEST_F(FindInPageManagerImplTest, ReturnLatestFind) {
+  auto frame_with_one_match = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(1.0), kOneMatchFrameId);
+  FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
+  auto frame_with_two_matches = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(2.0), kTwoMatchesFrameId);
+  FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
+  test_web_state_->AddWebFrame(std::move(frame_with_one_match));
+  test_web_state_->AddWebFrame(std::move(frame_with_two_matches));
+
+  GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
+
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_one_match_ptr->last_javascript_call());
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_two_matches_ptr->last_javascript_call());
+  test_web_state_->RemoveWebFrame(kOneMatchFrameId);
+  GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
+    base::RunLoop().RunUntilIdle();
+    return fake_delegate_.state();
+  }));
+  EXPECT_EQ(2, fake_delegate_.state()->match_count);
+}
+
+// Tests that Find In Page should not return if the web state is destroyed
+// during a find.
+TEST_F(FindInPageManagerImplTest, DestroyWebStateDuringFind) {
+  auto frame_with_one_match = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(1.0), kOneMatchFrameId);
+  FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
+  test_web_state_->AddWebFrame(std::move(frame_with_one_match));
+
+  GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
+
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_one_match_ptr->last_javascript_call());
+  test_web_state_ = nullptr;
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(fake_delegate_.state());
+}
+
+// Tests that Find in Page updates total match count when a frame with matches
+// becomes unavailable during find.
+TEST_F(FindInPageManagerImplTest, FrameUnavailableAfterDelegateCallback) {
+  auto frame_with_one_match = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(1.0), kOneMatchFrameId);
+  FakeWebFrame* frame_with_one_match_ptr = frame_with_one_match.get();
+  auto frame_with_two_matches = CreateWebFrameWithJsResultForFind(
+      std::make_unique<base::Value>(2.0), kTwoMatchesFrameId);
+  FakeWebFrame* frame_with_two_matches_ptr = frame_with_two_matches.get();
+  test_web_state_->AddWebFrame(std::move(frame_with_one_match));
+  test_web_state_->AddWebFrame(std::move(frame_with_two_matches));
+
+  GetFindInPageManager()->Find(@"foo", FindInPageOptions::FindInPageSearch);
+
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_one_match_ptr->last_javascript_call());
+  EXPECT_EQ("__gCrWeb.findInPage.findString(\"foo\", 100.0);",
+            frame_with_two_matches_ptr->last_javascript_call());
+  test_web_state_->RemoveWebFrame(kTwoMatchesFrameId);
+  ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
+    base::RunLoop().RunUntilIdle();
+    return fake_delegate_.state();
+  }));
+  EXPECT_EQ(1, fake_delegate_.state()->match_count);
+}
+
+}  // namespace web
diff --git a/ios/web/shell/test/BUILD.gn b/ios/web/shell/test/BUILD.gn
index 4cf9c16b..e05de3f 100644
--- a/ios/web/shell/test/BUILD.gn
+++ b/ios/web/shell/test/BUILD.gn
@@ -20,7 +20,6 @@
   sources = [
     "context_menu_egtest.mm",
     "page_state_egtest.mm",
-    "redirect_egtest.mm",
     "service_manager_egtest.mm",
   ]
 
diff --git a/ios/web/shell/test/redirect_egtest.mm b/ios/web/shell/test/redirect_egtest.mm
deleted file mode 100644
index 0c32da3..0000000
--- a/ios/web/shell/test/redirect_egtest.mm
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <EarlGrey/EarlGrey.h>
-
-#import "ios/web/public/test/http_server/html_response_provider.h"
-#import "ios/web/public/test/http_server/html_response_provider_impl.h"
-#import "ios/web/public/test/http_server/http_server.h"
-#include "ios/web/public/test/http_server/http_server_util.h"
-#import "ios/web/shell/test/earl_grey/shell_earl_grey.h"
-#import "ios/web/shell/test/earl_grey/shell_matchers.h"
-#import "ios/web/shell/test/earl_grey/shell_matchers_shorthand.h"
-#import "ios/web/shell/test/earl_grey/web_shell_test_case.h"
-#include "net/http/http_status_code.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using web::AddressFieldText;
-using web::test::HttpServer;
-
-// Redirect test cases for the web shell.
-@interface RedirectTestCase : WebShellTestCase
-@end
-
-@implementation RedirectTestCase
-
-// Tests loading of page that is redirected 3 times using a 301 redirect.
-- (void)testMultipleRedirects {
-  // Create map of canned responses and set up the test HTML server.
-  std::map<GURL, HtmlResponseProviderImpl::Response> responses;
-  const GURL firstRedirectURL = HttpServer::MakeUrl("http://firstRedirect/");
-  const GURL secondRedirectURL = HttpServer::MakeUrl("http://secondRedirect/");
-  const GURL thirdRedirectURL = HttpServer::MakeUrl("http://thirdRedirect/");
-  const GURL destinationURL = HttpServer::MakeUrl("http://destination/");
-
-  responses[firstRedirectURL] = HtmlResponseProviderImpl::GetRedirectResponse(
-      secondRedirectURL, net::HTTP_MOVED_PERMANENTLY);
-  responses[secondRedirectURL] = HtmlResponseProviderImpl::GetRedirectResponse(
-      thirdRedirectURL, net::HTTP_MOVED_PERMANENTLY);
-  responses[thirdRedirectURL] = HtmlResponseProviderImpl::GetRedirectResponse(
-      destinationURL, net::HTTP_MOVED_PERMANENTLY);
-  const char kFinalPageContent[] = "testMultipleRedirects complete";
-  responses[destinationURL] =
-      HtmlResponseProviderImpl::GetSimpleResponse(kFinalPageContent);
-  std::unique_ptr<web::DataResponseProvider> provider(
-      new HtmlResponseProvider(responses));
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load first URL and expect destination URL to load.
-  [ShellEarlGrey loadURL:firstRedirectURL];
-  [[EarlGrey selectElementWithMatcher:AddressFieldText(destinationURL.spec())]
-      assertWithMatcher:grey_notNil()];
-  [ShellEarlGrey waitForWebViewContainingText:kFinalPageContent];
-}
-
-// Tests simple 301 redirection.
-- (void)testRedirection301 {
-  // Create map of canned responses and set up the test HTML server.
-  std::map<GURL, HtmlResponseProviderImpl::Response> responses;
-  const GURL firstRedirectURL = HttpServer::MakeUrl("http://firstRedirect/");
-  const GURL destinationURL = HttpServer::MakeUrl("http://destination/");
-
-  responses[firstRedirectURL] = HtmlResponseProviderImpl::GetRedirectResponse(
-      destinationURL, net::HTTP_MOVED_PERMANENTLY);
-  const char kFinalPageContent[] = "testRedirection301 complete";
-  responses[destinationURL] =
-      HtmlResponseProviderImpl::GetSimpleResponse(kFinalPageContent);
-  std::unique_ptr<web::DataResponseProvider> provider(
-      new HtmlResponseProvider(responses));
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load first URL and expect destination URL to load.
-  [ShellEarlGrey loadURL:firstRedirectURL];
-  [[EarlGrey selectElementWithMatcher:AddressFieldText(destinationURL.spec())]
-      assertWithMatcher:grey_notNil()];
-  [ShellEarlGrey waitForWebViewContainingText:kFinalPageContent];
-}
-
-// Tests simple 302 redirection.
-- (void)testRedirection302 {
-  // Create map of canned responses and set up the test HTML server.
-  std::map<GURL, HtmlResponseProviderImpl::Response> responses;
-  const GURL firstRedirectURL = HttpServer::MakeUrl("http://firstRedirect/");
-  const GURL destinationURL = HttpServer::MakeUrl("http://destination/");
-
-  responses[firstRedirectURL] = HtmlResponseProviderImpl::GetRedirectResponse(
-      destinationURL, net::HTTP_FOUND);
-  const char kFinalPageContent[] = "testRedirection302 complete";
-  responses[destinationURL] =
-      HtmlResponseProviderImpl::GetSimpleResponse(kFinalPageContent);
-  std::unique_ptr<web::DataResponseProvider> provider(
-      new HtmlResponseProvider(responses));
-  web::test::SetUpHttpServer(std::move(provider));
-
-  // Load first URL and expect destination URL to load.
-  [ShellEarlGrey loadURL:firstRedirectURL];
-  [[EarlGrey selectElementWithMatcher:AddressFieldText(destinationURL.spec())]
-      assertWithMatcher:grey_notNil()];
-  [ShellEarlGrey waitForWebViewContainingText:kFinalPageContent];
-}
-
-@end
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index cff7966..1acaa6b7 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -43,7 +43,6 @@
 
 namespace web {
 class NavigationItem;
-class WebState;
 class WebStateImpl;
 }
 
@@ -56,8 +55,7 @@
 // TODO(stuartmorgan): Move all of the navigation APIs out of this class.
 @interface CRWWebController : NSObject <CRWJSInjectionEvaluator,
                                         CRWSessionControllerDelegate,
-                                        CRWTouchTrackingDelegate,
-                                        UIGestureRecognizerDelegate>
+                                        CRWTouchTrackingDelegate>
 
 // Whether or not a UIWebView is allowed to exist in this CRWWebController.
 // Defaults to NO; this should be enabled before attempting to access the view.
@@ -66,8 +64,6 @@
 @property(nonatomic, weak) id<CRWNativeContentProvider> nativeProvider;
 @property(nonatomic, weak) id<CRWSwipeRecognizerProvider>
     swipeRecognizerProvider;
-@property(nonatomic, readonly) web::WebState* webState;
-@property(nonatomic, readonly) web::WebStateImpl* webStateImpl;
 
 // The container view used to display content.  If the view has been purged due
 // to low memory, this will recreate it.
@@ -91,9 +87,6 @@
 // (nothing loaded) and 1.0 (fully loaded).
 @property(nonatomic, readonly) double loadingProgress;
 
-// Returns the x, y offset the content has been scrolled.
-@property(nonatomic, readonly) CGPoint scrollPosition;
-
 // YES if the web process backing WebView is believed to currently be crashed.
 @property(nonatomic, assign, getter=isWebProcessCrashed) BOOL webProcessCrashed;
 
@@ -105,6 +98,10 @@
 // back-forward list navigations.
 @property(nonatomic) BOOL allowsBackForwardNavigationGestures;
 
+// The receiver of JavaScripts.
+@property(nonatomic, strong, readonly)
+    CRWJSInjectionReceiver* jsInjectionReceiver;
+
 // Designated initializer. Initializes web controller with |webState|. The
 // calling code must retain the ownership of |webState|.
 - (instancetype)initWithWebState:(web::WebStateImpl*)webState;
@@ -155,9 +152,6 @@
 // navigation. |isRendererInitiated| is NO for browser-initiated navigation.
 - (void)reloadWithRendererInitiatedNavigation:(BOOL)isRendererInitiated;
 
-// Stops web view loading.
-- (void)stopLoading;
-
 // Loads the URL indicated by current session state.
 - (void)loadCurrentURLWithRendererInitiatedNavigation:(BOOL)rendererInitiated;
 
@@ -203,8 +197,6 @@
 // Removes |recognizer| from the web view.
 - (void)removeGestureRecognizerFromWebView:(UIGestureRecognizer*)recognizer;
 
-- (CRWJSInjectionReceiver*)jsInjectionReceiver;
-
 // Returns the native controller (if any) current mananging the content.
 - (id<CRWNativeContent>)nativeController;
 
@@ -236,6 +228,9 @@
 
 @interface CRWWebController (UsedOnlyForTesting)  // Testing or internal API.
 
+@property(nonatomic, readonly) web::WebState* webState;
+@property(nonatomic, readonly) web::WebStateImpl* webStateImpl;
+
 // Returns whether the user is interacting with the page.
 @property(nonatomic, readonly) BOOL userIsInteracting;
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 9c936f0..3723b93 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -222,12 +222,6 @@
 @end
 
 @implementation CRWWebControllerPendingNavigationInfo
-@synthesize referrer = _referrer;
-@synthesize MIMEType = _MIMEType;
-@synthesize navigationType = _navigationType;
-@synthesize HTTPMethod = _HTTPMethod;
-@synthesize cancelled = _cancelled;
-@synthesize hasUserGesture = _hasUserGesture;
 
 - (instancetype)init {
   if ((self = [super init])) {
@@ -246,8 +240,6 @@
                                CRWWebViewScrollViewProxyObserver,
                                WKNavigationDelegate,
                                WKUIDelegate> {
-  // The WKWebView managed by this instance.
-  WKWebView* _webView;
   // The view used to display content.  Must outlive |_webViewProxy|. The
   // container view should be accessed through this property rather than
   // |self.view| from within this class, as |self.view| triggers creation while
@@ -257,14 +249,8 @@
   // and after web usage was disabled. Used by |-loadCurrentURLIfNecessary| to
   // prevent extra loads.
   BOOL _currentURLLoadWasTrigerred;
-  // If |_contentView| contains a native view rather than a web view, this
-  // is its controller. If it's a web view, this is nil.
-  id<CRWNativeContent> _nativeController;
   BOOL _isHalted;  // YES if halted. Halting happens prior to destruction.
   BOOL _isBeingDestroyed;  // YES if in the process of closing.
-  // YES if a user interaction has been registered at any time once the page has
-  // loaded.
-  BOOL _userInteractionRegistered;
   // YES if the user has interacted with the content area since the last URL
   // change.
   BOOL _interactionRegisteredSinceLastURLChange;
@@ -287,9 +273,6 @@
   NSMutableArray* _gestureRecognizers;
   // Flag to say if browsing is enabled.
   BOOL _webUsageEnabled;
-  // The touch tracking recognizer allowing us to decide if a navigation is
-  // started by the user.
-  CRWTouchTrackingRecognizer* _touchTrackingRecognizer;
   // The controller that tracks long press and check context menu trigger.
   CRWContextMenuController* _contextMenuController;
   // Whether a click is in progress.
@@ -315,10 +298,6 @@
   // Object for loading POST requests with body.
   CRWJSPOSTRequestLoader* _POSTRequestLoader;
 
-  // WebStateImpl instance associated with this CRWWebController, web controller
-  // does not own this pointer.
-  WebStateImpl* _webStateImpl;
-
   // A set of script managers whose scripts have been injected into the current
   // page.
   // TODO(stuartmorgan): Revisit this approach; it's intended only as a stopgap
@@ -332,9 +311,6 @@
   // Script manager for setting the windowID.
   CRWJSWindowIDManager* _windowIDJSManager;
 
-  // The receiver of JavaScripts.
-  CRWJSInjectionReceiver* _jsInjectionReceiver;
-
   // Backs up property with the same name.
   std::unique_ptr<web::MojoFacade> _mojoFacade;
 
@@ -391,12 +367,6 @@
 @property(nonatomic, readwrite) web::PageDisplayState pageDisplayState;
 // The currently displayed native controller, if any.
 @property(weak, nonatomic, readwrite) id<CRWNativeContent> nativeController;
-// Returns NavigationManager's session controller.
-@property(weak, nonatomic, readonly) CRWSessionController* sessionController;
-// The associated NavigationManagerImpl.
-@property(nonatomic, readonly) NavigationManagerImpl* navigationManagerImpl;
-// Whether the associated WebState has an opener.
-@property(nonatomic, readonly) BOOL hasOpener;
 // Dictionary where keys are the names of WKWebView properties and values are
 // selector names which should be called when a corresponding property has
 // changed. e.g. @{ @"URL" : @"webViewURLDidChange" } means that
@@ -425,6 +395,31 @@
 // Facade for Mojo API.
 @property(nonatomic, readonly) web::MojoFacade* mojoFacade;
 
+// YES if a user interaction has been registered at any time since the page has
+// loaded.
+@property(nonatomic, readwrite) BOOL userInteractionRegistered;
+
+@property(nonatomic, readonly) web::WebState* webState;
+// WebStateImpl instance associated with this CRWWebController, web controller
+// does not own this pointer.
+@property(nonatomic, readonly) web::WebStateImpl* webStateImpl;
+
+// Returns the x, y offset the content has been scrolled.
+@property(nonatomic, readonly) CGPoint scrollPosition;
+
+// The touch tracking recognizer allowing us to decide if a navigation has user
+// gesture. Lazily created.
+@property(nonatomic, strong, readonly)
+    CRWTouchTrackingRecognizer* touchTrackingRecognizer;
+
+// Session Information
+// -------------------
+// Returns NavigationManager's session controller.
+@property(weak, nonatomic, readonly) CRWSessionController* sessionController;
+// The associated NavigationManagerImpl.
+@property(nonatomic, readonly) NavigationManagerImpl* navigationManagerImpl;
+// Whether the associated WebState has an opener.
+@property(nonatomic, readonly) BOOL hasOpener;
 // TODO(crbug.com/692871): Remove these functions and replace with more
 // appropriate NavigationItem getters.
 // Returns the navigation item for the current page.
@@ -437,10 +432,6 @@
 // unless the request was a POST.
 @property(weak, nonatomic, readonly) NSDictionary* currentHTTPHeaders;
 
-// YES if a user interaction has been registered at any time since the page has
-// loaded.
-@property(nonatomic, readwrite) BOOL userInteractionRegistered;
-
 // Called when the web page has changed document and/or URL, and so the page
 // navigation should be reported to the delegate, and internal state updated to
 // reflect the fact that the navigation has occurred. |context| contains
@@ -458,8 +449,6 @@
 - (void)didStartLoading;
 // Returns YES if the URL looks like it is one CRWWebController can show.
 + (BOOL)webControllerCanShow:(const GURL&)url;
-// Returns a lazily created CRWTouchTrackingRecognizer.
-- (CRWTouchTrackingRecognizer*)touchTrackingRecognizer;
 // Creates a container view if it's not yet created.
 - (void)ensureContainerViewCreated;
 // Creates a web view if it's not yet created.
@@ -659,14 +648,6 @@
 - (void)injectHTML5HistoryScriptWithHashChange:(BOOL)dispatchHashChange
                         sameDocumentNavigation:(BOOL)sameDocumentNavigation;
 
-// WKNavigation objects are used as a weak key to store web::NavigationContext.
-// WKWebView manages WKNavigation lifetime and destroys them after the
-// navigation is finished. However for window opening navigations WKWebView
-// passes null WKNavigation to WKNavigationDelegate callbacks and strong key is
-// used to store web::NavigationContext. Those "null" navigations have to be
-// cleaned up manually by calling this method.
-- (void)forgetNullWKNavigation:(WKNavigation*)navigation;
-
 // Returns YES if the current live view is a web view with an image MIME type.
 - (BOOL)contentIsImage;
 // Restores the state for this page from session history.
@@ -697,8 +678,6 @@
 // Sets scroll offset value for webview scroll view from |scrollState|.
 - (void)applyWebViewScrollOffsetFromScrollState:
     (const web::PageScrollState&)scrollState;
-// Returns the referrer for the current page.
-- (web::Referrer)currentReferrer;
 // Adds a new NavigationItem with the given URL and state object to the history
 // stack. A state object is a serialized generic JavaScript object that contains
 // details of the UI's state for a given NavigationItem/URL.
@@ -758,38 +737,9 @@
              completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
                                          NSURLCredential*))completionHandler;
 
-// Helper to respond to |webView:runJavaScript...| delegate methods.
-// |completionHandler| must not be nil.
-- (void)runJavaScriptDialogOfType:(web::JavaScriptDialogType)type
-                 initiatedByFrame:(WKFrameInfo*)frame
-                          message:(NSString*)message
-                      defaultText:(NSString*)defaultText
-                       completion:(void (^)(BOOL, NSString*))completionHandler;
-
-// Called when WKWebView estimatedProgress has been changed.
-- (void)webViewEstimatedProgressDidChange;
-// Called when WKWebView certificateChain or hasOnlySecureContent property has
-// changed.
-- (void)webViewSecurityFeaturesDidChange;
-// Called when WKWebView loading state has been changed.
-- (void)webViewLoadingStateDidChange;
-// Called when WKWebView title has been changed.
-- (void)webViewTitleDidChange;
-// Called when WKWebView canGoForward/canGoBack state has been changed.
-- (void)webViewBackForwardStateDidChange;
-// Called when WKWebView URL has been changed.
-- (void)webViewURLDidChange;
-// Returns YES if a KVO change to |newURL| could be a 'navigation' within the
-// document (hash change, pushState/replaceState, etc.). This should only be
-// used in the context of a URL KVO callback firing, and only if |isLoading| is
-// YES for the web view (since if it's not, no guesswork is needed).
-- (BOOL)isKVOChangePotentialSameDocumentNavigationToURL:(const GURL&)newURL;
 // Returns YES if a SafeBrowsing warning is currently displayed within
 // WKWebView.
 - (BOOL)isSafeBrowsingWarningDisplayedInWebView;
-// Called when a non-document-changing URL change occurs. Updates the
-// _documentURL, and informs the superclass of the change.
-- (void)URLDidChangeWithoutDocumentChange:(const GURL&)URL;
 // Returns context for pending navigation that has |URL|. null if there is no
 // matching pending navigation.
 - (web::NavigationContextImpl*)contextForPendingMainFrameNavigationWithURL:
@@ -922,15 +872,10 @@
 
 @implementation CRWWebController
 
-@synthesize webUsageEnabled = _webUsageEnabled;
-@synthesize loadPhase = _loadPhase;
-@synthesize webProcessCrashed = _webProcessCrashed;
-@synthesize visible = _visible;
-@synthesize nativeProvider = _nativeProvider;
-@synthesize swipeRecognizerProvider = _swipeRecognizerProvider;
-@synthesize webViewProxy = _webViewProxy;
-@synthesize allowsBackForwardNavigationGestures =
-    _allowsBackForwardNavigationGestures;
+// Synthesize as it is readonly.
+@synthesize touchTrackingRecognizer = _touchTrackingRecognizer;
+
+#pragma mark - Object lifecycle
 
 - (instancetype)initWithWebState:(WebStateImpl*)webState {
   self = [super init];
@@ -970,38 +915,121 @@
   return self;
 }
 
-- (WebState*)webState {
-  return _webStateImpl;
-}
-
-- (WebStateImpl*)webStateImpl {
-  return _webStateImpl;
-}
-
-- (void)clearTransientContentView {
-  // Early return if there is no transient content view.
-  if (![_containerView transientContentView])
-    return;
-
-  // Remove the transient content view from the hierarchy.
-  [_containerView clearTransientContentView];
-}
-
-- (void)showTransientContentView:(CRWContentView*)contentView {
-  DCHECK(contentView);
-  DCHECK(contentView.scrollView);
-  // TODO(crbug.com/556848) Reenable DCHECK when |CRWWebControllerContainerView|
-  // is restructured so that subviews are not added during |layoutSubviews|.
-  // DCHECK([contentView.scrollView isDescendantOfView:contentView]);
-  [_containerView displayTransientContent:contentView];
-}
-
 - (void)dealloc {
   DCHECK([NSThread isMainThread]);
   DCHECK(_isBeingDestroyed);  // 'close' must have been called already.
   DCHECK(!_webView);
 }
 
+#pragma mark - Public property accessors
+
+- (void)setWebUsageEnabled:(BOOL)enabled {
+  if (_webUsageEnabled == enabled)
+    return;
+  // WKWebView autoreleases its WKProcessPool on removal from superview.
+  // Deferring WKProcessPool deallocation may lead to issues with cookie
+  // clearing and and Browsing Data Partitioning implementation.
+  @autoreleasepool {
+    if (!enabled) {
+      [self removeWebView];
+    }
+  }
+
+  _webUsageEnabled = enabled;
+
+  // WKWebView autoreleases its WKProcessPool on removal from superview.
+  // Deferring WKProcessPool deallocation may lead to issues with cookie
+  // clearing and and Browsing Data Partitioning implementation.
+  @autoreleasepool {
+    [self setNativeControllerWebUsageEnabled:_webUsageEnabled];
+    if (enabled) {
+      // Don't create the web view; let it be lazy created as needed.
+    } else {
+      self.webStateImpl->ClearTransientContent();
+      _touchTrackingRecognizer.touchTrackingDelegate = nil;
+      _touchTrackingRecognizer = nil;
+      _currentURLLoadWasTrigerred = NO;
+    }
+  }
+}
+
+- (UIView*)view {
+  [self ensureContainerViewCreated];
+  DCHECK(_containerView);
+  return _containerView;
+}
+
+- (id<CRWWebViewNavigationProxy>)webViewNavigationProxy {
+  return static_cast<id<CRWWebViewNavigationProxy>>(self.webView);
+}
+
+- (UIView*)viewForPrinting {
+  // Printing is not supported for native controllers.
+  return self.webView;
+}
+
+- (double)loadingProgress {
+  return [self.webView estimatedProgress];
+}
+
+- (void)setAllowsBackForwardNavigationGestures:
+    (BOOL)allowsBackForwardNavigationGestures {
+  // Store it to an instance variable as well as
+  // self.webView.allowsBackForwardNavigationGestures because self.webView may
+  // be nil. When self.webView is nil, it will be set later in -setWebView:.
+  _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
+  self.webView.allowsBackForwardNavigationGestures =
+      allowsBackForwardNavigationGestures;
+}
+
+#pragma mark - Private properties accessors
+
+- (UIScrollView*)webScrollView {
+  return self.webView.scrollView;
+}
+
+- (web::PageDisplayState)pageDisplayState {
+  web::PageDisplayState displayState;
+  // If a native controller is present, record its display state instead of that
+  // of the underlying placeholder webview.
+  if (self.nativeController) {
+    if ([self.nativeController respondsToSelector:@selector(contentOffset)]) {
+      displayState.scroll_state().set_content_offset(
+          [self.nativeController contentOffset]);
+    }
+    if ([self.nativeController respondsToSelector:@selector(contentInset)]) {
+      displayState.scroll_state().set_content_inset(
+          [self.nativeController contentInset]);
+    }
+  } else if (self.webView) {
+    displayState.set_scroll_state(web::PageScrollState(
+        self.scrollPosition, self.webScrollView.contentInset));
+    UIScrollView* scrollView = self.webScrollView;
+    displayState.zoom_state().set_minimum_zoom_scale(
+        scrollView.minimumZoomScale);
+    displayState.zoom_state().set_maximum_zoom_scale(
+        scrollView.maximumZoomScale);
+    displayState.zoom_state().set_zoom_scale(scrollView.zoomScale);
+  }
+  return displayState;
+}
+
+- (void)setPageDisplayState:(web::PageDisplayState)displayState {
+  if (!displayState.IsValid())
+    return;
+  if (self.webView) {
+    // Page state is restored after a page load completes.  If the user has
+    // scrolled or changed the zoom scale while the page is still loading, don't
+    // restore any state since it will confuse the user.
+    web::PageDisplayState currentPageDisplayState = self.pageDisplayState;
+    if (currentPageDisplayState.scroll_state() ==
+            _displayStateOnStartLoading.scroll_state() &&
+        !_pageHasZoomed) {
+      [self applyPageDisplayState:displayState];
+    }
+  }
+}
+
 - (id<CRWNativeContent>)nativeController {
   return [_containerView nativeController];
 }
@@ -1032,185 +1060,6 @@
   };
 }
 
-// NativeControllerDelegate method, called to inform that title has changed.
-- (void)nativeContent:(id)content titleDidChange:(NSString*)title {
-  [self setNavigationItemTitle:title];
-}
-
-- (void)setNativeControllerWebUsageEnabled:(BOOL)webUsageEnabled {
-  if ([self.nativeController
-          respondsToSelector:@selector(setWebUsageEnabled:)]) {
-    [self.nativeController setWebUsageEnabled:webUsageEnabled];
-  }
-}
-
-- (void)setWebUsageEnabled:(BOOL)enabled {
-  if (_webUsageEnabled == enabled)
-    return;
-  // WKWebView autoreleases its WKProcessPool on removal from superview.
-  // Deferring WKProcessPool deallocation may lead to issues with cookie
-  // clearing and and Browsing Data Partitioning implementation.
-  @autoreleasepool {
-    if (!enabled) {
-      [self removeWebView];
-    }
-  }
-
-  _webUsageEnabled = enabled;
-
-  // WKWebView autoreleases its WKProcessPool on removal from superview.
-  // Deferring WKProcessPool deallocation may lead to issues with cookie
-  // clearing and and Browsing Data Partitioning implementation.
-  @autoreleasepool {
-    [self setNativeControllerWebUsageEnabled:_webUsageEnabled];
-    if (enabled) {
-      // Don't create the web view; let it be lazy created as needed.
-    } else {
-      _webStateImpl->ClearTransientContent();
-      _touchTrackingRecognizer.touchTrackingDelegate = nil;
-      _touchTrackingRecognizer = nil;
-      _currentURLLoadWasTrigerred = NO;
-    }
-  }
-}
-
-- (void)requirePageReconstruction {
-  // TODO(crbug.com/736103): Removing web view will destroy session history for
-  // WKBasedNavigationManager.
-  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled())
-    [self removeWebView];
-}
-
-- (BOOL)isViewAlive {
-  return !_webProcessCrashed && [_containerView isViewAlive];
-}
-
-- (BOOL)isSafeBrowsingWarningDisplayedInWebView {
-  // A SafeBrowsing warning is a UIScrollView that is inserted on top of
-  // WKWebView's scroll view. This method uses heuristics to detect this view.
-  // It may break in the future if WebKit's implementation of SafeBrowsing
-  // warnings changes.
-  UIView* containingView = _webView.scrollView.superview;
-  if (!containingView)
-    return NO;
-
-  UIView* topView = containingView.subviews.lastObject;
-
-  if (topView == _webView.scrollView)
-    return NO;
-
-  return
-      [topView isKindOfClass:[UIScrollView class]] &&
-      [NSStringFromClass([topView class]) containsString:@"Warning"] &&
-      topView.subviews.count > 0 &&
-      [topView.subviews[0].subviews.lastObject isKindOfClass:[UIButton class]];
-}
-
-- (BOOL)contentIsHTML {
-  if (!_webView)
-    return NO;
-
-  std::string MIMEType = self.webState->GetContentsMimeType();
-  return MIMEType == "text/html" || MIMEType == "application/xhtml+xml" ||
-         MIMEType == "application/xml";
-}
-
-// Stop doing stuff, especially network stuff. Close the request tracker.
-- (void)terminateNetworkActivity {
-  DCHECK(!_isHalted);
-  _isHalted = YES;
-
-  // Cancel all outstanding perform requests, and clear anything already queued
-  // (since this may be called from within the handling loop) to prevent any
-  // asynchronous JavaScript invocation handling from continuing.
-  [NSObject cancelPreviousPerformRequestsWithTarget:self];
-}
-
-- (void)dismissModals {
-  if ([self.nativeController respondsToSelector:@selector(dismissModals)])
-    [self.nativeController dismissModals];
-}
-
-// Caller must reset the delegate before calling.
-- (void)close {
-  _webStateImpl->CancelDialogs();
-
-  _SSLStatusUpdater = nil;
-
-  self.nativeProvider = nil;
-  self.swipeRecognizerProvider = nil;
-  if ([self.nativeController respondsToSelector:@selector(close)])
-    [self.nativeController close];
-
-  if (!_isHalted) {
-    [self terminateNetworkActivity];
-  }
-
-  // Mark the destruction sequence has started, in case someone else holds a
-  // strong reference and tries to continue using the tab.
-  DCHECK(!_isBeingDestroyed);
-  _isBeingDestroyed = YES;
-
-  // Remove the web view now. Otherwise, delegate callbacks occur.
-  [self removeWebView];
-
-  // Explicitly reset content to clean up views and avoid dangling KVO
-  // observers.
-  [_containerView resetContent];
-
-  _webStateImpl = nullptr;
-
-  DCHECK(!_webView);
-  // TODO(crbug.com/662860): Don't set the delegate to nil.
-  [_containerView setDelegate:nil];
-  if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) {
-    [self.nativeController setDelegate:nil];
-  }
-  _touchTrackingRecognizer.touchTrackingDelegate = nil;
-  [[_webViewProxy scrollViewProxy] removeObserver:self];
-  [[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
-- (CGPoint)scrollPosition {
-  return self.webScrollView.contentOffset;
-}
-
-- (GURL)currentURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel {
-  DCHECK(trustLevel) << "Verification of the trustLevel state is mandatory";
-
-  // The web view URL is the current URL only if it is neither a placeholder URL
-  // (used to hold WKBackForwardListItem for WebUI and Native Content views) nor
-  // a restore_session.html (used to replay session history in WKWebView).
-  // TODO(crbug.com/738020): Investigate if this method is still needed and if
-  // it can be implemented using NavigationManager API after removal of legacy
-  // navigation stack.
-  GURL webViewURL = net::GURLWithNSURL(_webView.URL);
-  if (_webView && !IsWKInternalUrl(webViewURL)) {
-    return [self webURLWithTrustLevel:trustLevel];
-  }
-  // Any non-web URL source is trusted.
-  *trustLevel = web::URLVerificationTrustLevel::kAbsolute;
-  if (self.nativeController) {
-    if ([self.nativeController respondsToSelector:@selector(virtualURL)]) {
-      return [self.nativeController virtualURL];
-    } else {
-      return [self.nativeController url];
-    }
-  }
-  web::NavigationItem* item =
-      self.navigationManagerImpl
-          ->GetLastCommittedItemInCurrentOrRestoredSession();
-  return item ? item->GetVirtualURL() : GURL::EmptyGURL();
-}
-
-- (WKWebView*)webView {
-  return _webView;
-}
-
-- (UIScrollView*)webScrollView {
-  return [_webView scrollView];
-}
-
 - (GURL)currentURL {
   web::URLVerificationTrustLevel trustLevel =
       web::URLVerificationTrustLevel::kNone;
@@ -1248,20 +1097,708 @@
                        web::ReferrerPolicyAlways);
 }
 
+- (BOOL)userClickedRecently {
+  // Scrolling generates a pair of touch on/off event which causes
+  // _lastUserInteraction to register that there was user interaction.
+  // Checks for scrolling first to override time-based click heuristics.
+  BOOL scrolling = [[self webScrollView] isDragging] ||
+                   [[self webScrollView] isDecelerating];
+  if (scrolling)
+    return NO;
+  if (!_lastUserInteraction)
+    return NO;
+  return _clickInProgress ||
+         ((CFAbsoluteTimeGetCurrent() - _lastUserInteraction->time) <
+          kMaximumDelayForUserInteractionInSeconds);
+}
+
+- (web::UserAgentType)userAgentType {
+  web::NavigationItem* item = self.currentNavItem;
+  return item ? item->GetUserAgentType() : web::UserAgentType::MOBILE;
+}
+
+- (web::MojoFacade*)mojoFacade {
+  if (!_mojoFacade) {
+    service_manager::mojom::InterfaceProvider* interfaceProvider =
+        self.webStateImpl->GetWebStateInterfaceProvider();
+    _mojoFacade.reset(new web::MojoFacade(interfaceProvider, self));
+  }
+  return _mojoFacade.get();
+}
+
+- (void)setUserInteractionRegistered:(BOOL)flag {
+  _userInteractionRegistered = flag;
+  if (flag)
+    _interactionRegisteredSinceLastURLChange = YES;
+}
+
+- (WebState*)webState {
+  return _webStateImpl;
+}
+
+- (CGPoint)scrollPosition {
+  return self.webScrollView.contentOffset;
+}
+
+- (CRWTouchTrackingRecognizer*)touchTrackingRecognizer {
+  if (!_touchTrackingRecognizer) {
+    _touchTrackingRecognizer =
+        [[CRWTouchTrackingRecognizer alloc] initWithDelegate:self];
+  }
+  return _touchTrackingRecognizer;
+}
+
+#pragma mark Session Information
+
+- (CRWSessionController*)sessionController {
+  NavigationManagerImpl* navigationManager = self.navigationManagerImpl;
+  return navigationManager ? navigationManager->GetSessionController() : nil;
+}
+
+- (NavigationManagerImpl*)navigationManagerImpl {
+  return self.webStateImpl ? &(self.webStateImpl->GetNavigationManagerImpl())
+                           : nil;
+}
+
+- (BOOL)hasOpener {
+  return self.webStateImpl ? self.webStateImpl->HasOpener() : NO;
+}
+
+- (web::NavigationItemImpl*)currentNavItem {
+  return self.navigationManagerImpl
+             ? self.navigationManagerImpl->GetCurrentItemImpl()
+             : nullptr;
+}
+
+- (ui::PageTransition)currentTransition {
+  if (self.currentNavItem)
+    return self.currentNavItem->GetTransitionType();
+  else
+    return ui::PageTransitionFromInt(0);
+}
+
+- (web::Referrer)currentNavItemReferrer {
+  web::NavigationItem* currentItem = self.currentNavItem;
+  return currentItem ? currentItem->GetReferrer() : web::Referrer();
+}
+
+- (NSDictionary*)currentHTTPHeaders {
+  web::NavigationItem* currentItem = self.currentNavItem;
+  return currentItem ? currentItem->GetHttpRequestHeaders() : nil;
+}
+
+#pragma mark - ** Public Methods **
+
+#pragma mark - Header public methods
+
+- (void)showTransientContentView:(CRWContentView*)contentView {
+  DCHECK(contentView);
+  DCHECK(contentView.scrollView);
+  // TODO(crbug.com/556848) Reenable DCHECK when |CRWWebControllerContainerView|
+  // is restructured so that subviews are not added during |layoutSubviews|.
+  // DCHECK([contentView.scrollView isDescendantOfView:contentView]);
+  [_containerView displayTransientContent:contentView];
+}
+
+- (void)clearTransientContentView {
+  // Early return if there is no transient content view.
+  if (![_containerView transientContentView])
+    return;
+
+  // Remove the transient content view from the hierarchy.
+  [_containerView clearTransientContentView];
+}
+
+// Stop doing stuff, especially network stuff. Close the request tracker.
+- (void)terminateNetworkActivity {
+  DCHECK(!_isHalted);
+  _isHalted = YES;
+
+  // Cancel all outstanding perform requests, and clear anything already queued
+  // (since this may be called from within the handling loop) to prevent any
+  // asynchronous JavaScript invocation handling from continuing.
+  [NSObject cancelPreviousPerformRequestsWithTarget:self];
+}
+
+- (void)dismissModals {
+  if ([self.nativeController respondsToSelector:@selector(dismissModals)])
+    [self.nativeController dismissModals];
+}
+
+// Caller must reset the delegate before calling.
+- (void)close {
+  self.webStateImpl->CancelDialogs();
+
+  _SSLStatusUpdater = nil;
+
+  self.nativeProvider = nil;
+  self.swipeRecognizerProvider = nil;
+  if ([self.nativeController respondsToSelector:@selector(close)])
+    [self.nativeController close];
+
+  if (!_isHalted) {
+    [self terminateNetworkActivity];
+  }
+
+  // Mark the destruction sequence has started, in case someone else holds a
+  // strong reference and tries to continue using the tab.
+  DCHECK(!_isBeingDestroyed);
+  _isBeingDestroyed = YES;
+
+  // Remove the web view now. Otherwise, delegate callbacks occur.
+  [self removeWebView];
+
+  // Explicitly reset content to clean up views and avoid dangling KVO
+  // observers.
+  [_containerView resetContent];
+
+  _webStateImpl = nullptr;
+
+  DCHECK(!self.webView);
+  // TODO(crbug.com/662860): Don't set the delegate to nil.
+  [_containerView setDelegate:nil];
+  if ([self.nativeController respondsToSelector:@selector(setDelegate:)]) {
+    [self.nativeController setDelegate:nil];
+  }
+  _touchTrackingRecognizer.touchTrackingDelegate = nil;
+  [[_webViewProxy scrollViewProxy] removeObserver:self];
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (BOOL)isViewAlive {
+  return !_webProcessCrashed && [_containerView isViewAlive];
+}
+
+- (BOOL)contentIsHTML {
+  if (!self.webView)
+    return NO;
+
+  std::string MIMEType = self.webState->GetContentsMimeType();
+  return MIMEType == "text/html" || MIMEType == "application/xhtml+xml" ||
+         MIMEType == "application/xml";
+}
+
+- (GURL)currentURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel {
+  DCHECK(trustLevel) << "Verification of the trustLevel state is mandatory";
+
+  // The web view URL is the current URL only if it is neither a placeholder URL
+  // (used to hold WKBackForwardListItem for WebUI and Native Content views) nor
+  // a restore_session.html (used to replay session history in WKWebView).
+  // TODO(crbug.com/738020): Investigate if this method is still needed and if
+  // it can be implemented using NavigationManager API after removal of legacy
+  // navigation stack.
+  GURL webViewURL = net::GURLWithNSURL(self.webView.URL);
+  if (self.webView && !IsWKInternalUrl(webViewURL)) {
+    return [self webURLWithTrustLevel:trustLevel];
+  }
+  // Any non-web URL source is trusted.
+  *trustLevel = web::URLVerificationTrustLevel::kAbsolute;
+  if (self.nativeController) {
+    if ([self.nativeController respondsToSelector:@selector(virtualURL)]) {
+      return [self.nativeController virtualURL];
+    } else {
+      return [self.nativeController url];
+    }
+  }
+  web::NavigationItem* item =
+      self.navigationManagerImpl
+          ->GetLastCommittedItemInCurrentOrRestoredSession();
+  return item ? item->GetVirtualURL() : GURL::EmptyGURL();
+}
+
+- (void)reloadWithRendererInitiatedNavigation:(BOOL)rendererInitiated {
+  // Clear last user interaction.
+  // TODO(crbug.com/546337): Move to after the load commits, in the subclass
+  // implementation. This will be inaccurate if the reload fails or is
+  // cancelled.
+  _lastUserInteraction = nullptr;
+  base::RecordAction(base::UserMetricsAction("Reload"));
+  GURL URL = self.currentNavItem->GetURL();
+  if ([self shouldLoadURLInNativeView:URL]) {
+    std::unique_ptr<web::NavigationContextImpl> navigationContext = [self
+        registerLoadRequestForURL:URL
+                         referrer:self.currentNavItemReferrer
+                       transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
+           sameDocumentNavigation:NO
+                   hasUserGesture:YES
+                rendererInitiated:rendererInitiated
+            placeholderNavigation:NO];
+    self.webStateImpl->OnNavigationStarted(navigationContext.get());
+    [self didStartLoading];
+    self.navigationManagerImpl->CommitPendingItem(
+        navigationContext->ReleaseItem());
+    [self.nativeController reload];
+    navigationContext->SetHasCommitted(true);
+    self.webStateImpl->OnNavigationFinished(navigationContext.get());
+    [self loadCompleteWithSuccess:YES forContext:nullptr];
+  } else {
+    web::NavigationItem* transientItem =
+        self.navigationManagerImpl->GetTransientItem();
+    if (transientItem) {
+      // If there's a transient item, a reload is considered a new navigation to
+      // the transient item's URL (as on other platforms).
+      NavigationManager::WebLoadParams reloadParams(transientItem->GetURL());
+      reloadParams.transition_type = ui::PAGE_TRANSITION_RELOAD;
+      reloadParams.extra_headers =
+          [transientItem->GetHttpRequestHeaders() copy];
+      self.webState->GetNavigationManager()->LoadURLWithParams(reloadParams);
+    } else {
+      self.currentNavItem->SetTransitionType(
+          ui::PageTransition::PAGE_TRANSITION_RELOAD);
+      if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+          !web::GetWebClient()->IsAppSpecificURL(
+              net::GURLWithNSURL(self.webView.URL))) {
+        // New navigation manager can delegate directly to WKWebView to reload
+        // for non-app-specific URLs. The necessary navigation states will be
+        // updated in WKNavigationDelegate callbacks.
+        WKNavigation* navigation = [self.webView reload];
+        [_navigationStates setState:web::WKNavigationState::REQUESTED
+                      forNavigation:navigation];
+        std::unique_ptr<web::NavigationContextImpl> navigationContext = [self
+            registerLoadRequestForURL:URL
+                             referrer:self.currentNavItemReferrer
+                           transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
+               sameDocumentNavigation:NO
+                       hasUserGesture:YES
+                    rendererInitiated:rendererInitiated
+                placeholderNavigation:NO];
+        [_navigationStates setContext:std::move(navigationContext)
+                        forNavigation:navigation];
+      } else {
+        [self loadCurrentURLWithRendererInitiatedNavigation:rendererInitiated];
+      }
+    }
+  }
+}
+
+- (void)stopLoading {
+  _stoppedWKNavigation = [_navigationStates lastAddedNavigation];
+
+  base::RecordAction(base::UserMetricsAction("Stop"));
+  // Discard the pending and transient entried before notifying the tab model
+  // observers of the change via |-abortLoad|.
+  self.navigationManagerImpl->DiscardNonCommittedItems();
+  [self abortLoad];
+  web::NavigationItem* item = self.currentNavItem;
+  GURL navigationURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
+  // If discarding the non-committed entries results in an app-specific URL,
+  // reload it in its native view.
+  if (!self.nativeController &&
+      [self shouldLoadURLInNativeView:navigationURL]) {
+    // RendererInitiated flag is meaningless for showing previous native
+    // content page. RendererInitiated is used as less previledged.
+    [self loadCurrentURLInNativeViewWithRendererInitiatedNavigation:YES];
+  }
+}
+
+- (void)loadCurrentURLWithRendererInitiatedNavigation:(BOOL)rendererInitiated {
+  // If the content view doesn't exist, the tab has either been evicted, or
+  // never displayed. Bail, and let the URL be loaded when the tab is shown.
+  if (!_containerView)
+    return;
+
+  // WKBasedNavigationManagerImpl needs WKWebView to load native views, but
+  // WKWebView cannot be created while web usage is disabled to avoid breaking
+  // clearing browser data. Bail now and let the URL be loaded when web
+  // usage is enabled again. This can happen when purging web pages when an
+  // interstitial is presented over a native view. See https://crbug.com/865985
+  // for details.
+  if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+      !_webUsageEnabled)
+    return;
+
+  _currentURLLoadWasTrigerred = YES;
+
+  // Reset current WebUI if one exists.
+  [self clearWebUI];
+
+  // Abort any outstanding page load. This ensures the delegate gets informed
+  // about the outgoing page, and further messages from the page are suppressed.
+  if (_loadPhase != web::PAGE_LOADED)
+    [self abortLoad];
+
+  DCHECK(!_isHalted);
+  self.webStateImpl->ClearTransientContent();
+
+  web::NavigationItem* item = self.currentNavItem;
+  const GURL currentURL = item ? item->GetURL() : GURL::EmptyGURL();
+  const bool isCurrentURLAppSpecific =
+      web::GetWebClient()->IsAppSpecificURL(currentURL);
+  // If it's a chrome URL, but not a native one, create the WebUI instance.
+  if (isCurrentURLAppSpecific &&
+      ![_nativeProvider hasControllerForURL:currentURL]) {
+    if (!(item->GetTransitionType() & ui::PAGE_TRANSITION_TYPED ||
+          item->GetTransitionType() & ui::PAGE_TRANSITION_AUTO_BOOKMARK) &&
+        self.hasOpener) {
+      // WebUI URLs can not be opened by DOM to prevent cross-site scripting as
+      // they have increased power. WebUI URLs may only be opened when the user
+      // types in the URL or use bookmarks.
+      self.navigationManagerImpl->DiscardNonCommittedItems();
+      return;
+    } else {
+      [self createWebUIForURL:currentURL];
+    }
+  }
+
+  // Loading a new url, must check here if it's a native chrome URL and
+  // replace the appropriate view if so, or transition back to a web view from
+  // a native view.
+  if ([self shouldLoadURLInNativeView:currentURL]) {
+    [self loadCurrentURLInNativeViewWithRendererInitiatedNavigation:
+              rendererInitiated];
+  } else if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
+             isCurrentURLAppSpecific && self.webStateImpl->HasWebUI() &&
+             !web::features::WebUISchemeHandlingEnabled()) {
+    [self loadPlaceholderInWebViewForURL:currentURL
+                       rendererInitiated:rendererInitiated
+                              forContext:nullptr];
+  } else {
+    [self loadCurrentURLInWebView];
+  }
+}
+
+- (void)loadCurrentURLIfNecessary {
+  if (_webProcessCrashed) {
+    [self loadCurrentURLWithRendererInitiatedNavigation:NO];
+  } else if (!_currentURLLoadWasTrigerred) {
+    [self ensureContainerViewCreated];
+
+    // This method reloads last committed item, so make than item also pending.
+    self.sessionController.pendingItemIndex =
+        self.sessionController.lastCommittedItemIndex;
+
+    // TODO(crbug.com/796608): end the practice of calling |loadCurrentURL|
+    // when it is possible there is no current URL. If the call performs
+    // necessary initialization, break that out.
+    [self loadCurrentURLWithRendererInitiatedNavigation:NO];
+  }
+}
+
+- (void)loadData:(NSData*)data
+        MIMEType:(NSString*)MIMEType
+          forURL:(const GURL&)URL {
+  web::NavigationItemImpl* item =
+      self.navigationManagerImpl->GetLastCommittedItemImpl();
+  auto navigationContext = web::NavigationContextImpl::CreateNavigationContext(
+      self.webStateImpl, URL,
+      /*has_user_gesture=*/true, item->GetTransitionType(),
+      /*is_renderer_initiated=*/false);
+  _loadPhase = web::LOAD_REQUESTED;
+  navigationContext->SetNavigationItemUniqueID(item->GetUniqueID());
+
+  item->SetNavigationInitiationType(
+      web::NavigationInitiationType::BROWSER_INITIATED);
+  // The error_retry_state_machine may still be in the
+  // |kDisplayingWebErrorForFailedNavigation| from the navigation that is being
+  // replaced. As the navigation is now successful, the error can be cleared.
+  item->error_retry_state_machine().SetNoNavigationError();
+  // The load data call will replace the current navigation and the webView URL
+  // of the navigation will be replaced by |URL|. Set the URL of the
+  // navigationItem to keep them synced.
+  // Note: it is possible that the URL in item already match |url|. But item can
+  // also contain a placeholder URL intended to be replaced.
+  item->SetURL(URL);
+  navigationContext->SetMimeType(MIMEType);
+  if (item->GetUserAgentType() == web::UserAgentType::NONE &&
+      web::wk_navigation_util::URLNeedsUserAgentType(URL)) {
+    item->SetUserAgentType(web::UserAgentType::MOBILE);
+  }
+
+  WKNavigation* navigation =
+      [self.webView loadData:data
+                       MIMEType:MIMEType
+          characterEncodingName:base::SysUTF8ToNSString(base::kCodepageUTF8)
+                        baseURL:net::NSURLWithGURL(URL)];
+
+  [_navigationStates setContext:std::move(navigationContext)
+                  forNavigation:navigation];
+  [_navigationStates setState:web::WKNavigationState::REQUESTED
+                forNavigation:navigation];
+}
+
+- (void)loadHTML:(NSString*)HTML forAppSpecificURL:(const GURL&)URL {
+  CHECK(web::GetWebClient()->IsAppSpecificURL(URL));
+  [self loadHTML:HTML forURL:URL];
+}
+
+- (void)executeUserJavaScript:(NSString*)script
+            completionHandler:(web::JavaScriptResultBlock)completion {
+  // For security reasons, executing JavaScript on pages with app-specific URLs
+  // is not allowed, because those pages may have elevated privileges.
+  GURL lastCommittedURL = self.webState->GetLastCommittedURL();
+  if (web::GetWebClient()->IsAppSpecificURL(lastCommittedURL)) {
+    if (completion) {
+      dispatch_async(dispatch_get_main_queue(), ^{
+        NSError* error = [[NSError alloc]
+            initWithDomain:web::kJSEvaluationErrorDomain
+                      code:web::JS_EVALUATION_ERROR_CODE_NO_WEB_VIEW
+                  userInfo:nil];
+        completion(nil, error);
+      });
+    }
+    return;
+  }
+
+  [self touched:YES];
+  [self executeJavaScript:script completionHandler:completion];
+}
+
+- (void)requirePageReconstruction {
+  // TODO(crbug.com/736103): Removing web view will destroy session history for
+  // WKBasedNavigationManager.
+  if (!web::GetWebClient()->IsSlimNavigationManagerEnabled())
+    [self removeWebView];
+}
+
+- (void)recordStateInHistory {
+  // Only record the state if:
+  // - the current NavigationItem's URL matches the current URL, and
+  // - the user has interacted with the page.
+  web::NavigationItem* item = self.currentNavItem;
+  if (item && item->GetURL() == [self currentURL] &&
+      self.userInteractionRegistered) {
+    item->SetPageDisplayState(self.pageDisplayState);
+  }
+}
+
+- (void)wasShown {
+  self.visible = YES;
+  if ([self.nativeController respondsToSelector:@selector(wasShown)]) {
+    [self.nativeController wasShown];
+  }
+}
+
+- (void)wasHidden {
+  self.visible = NO;
+  if (_isHalted)
+    return;
+  [self recordStateInHistory];
+  if ([self.nativeController respondsToSelector:@selector(wasHidden)]) {
+    [self.nativeController wasHidden];
+  }
+}
+
+- (void)addGestureRecognizerToWebView:(UIGestureRecognizer*)recognizer {
+  if ([_gestureRecognizers containsObject:recognizer])
+    return;
+
+  [self.webView addGestureRecognizer:recognizer];
+  [_gestureRecognizers addObject:recognizer];
+}
+
+- (void)removeGestureRecognizerFromWebView:(UIGestureRecognizer*)recognizer {
+  if (![_gestureRecognizers containsObject:recognizer])
+    return;
+
+  [self.webView removeGestureRecognizer:recognizer];
+  [_gestureRecognizers removeObject:recognizer];
+}
+
+- (void)didFinishGoToIndexSameDocumentNavigationWithType:
+            (web::NavigationInitiationType)type
+                                          hasUserGesture:(BOOL)hasUserGesture {
+  web::NavigationItem* item =
+      self.webStateImpl->GetNavigationManager()->GetLastCommittedItem();
+  GURL URL = item->GetVirtualURL();
+  std::unique_ptr<web::NavigationContextImpl> context =
+      web::NavigationContextImpl::CreateNavigationContext(
+          self.webStateImpl, URL, hasUserGesture,
+          static_cast<ui::PageTransition>(
+              item->GetTransitionType() |
+              ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK),
+          type == web::NavigationInitiationType::RENDERER_INITIATED);
+  context->SetIsSameDocument(true);
+  self.webStateImpl->SetIsLoading(true);
+  self.webStateImpl->OnNavigationStarted(context.get());
+  [self updateHTML5HistoryState];
+  [self setDocumentURL:URL context:context.get()];
+  context->SetHasCommitted(true);
+  self.webStateImpl->OnNavigationFinished(context.get());
+  [self didFinishWithURL:URL loadSuccess:YES context:context.get()];
+}
+
+- (void)goToBackForwardListItem:(WKBackForwardListItem*)wk_item
+                 navigationItem:(web::NavigationItem*)item
+       navigationInitiationType:(web::NavigationInitiationType)type
+                 hasUserGesture:(BOOL)hasUserGesture {
+  WKNavigation* navigation = [self.webView goToBackForwardListItem:wk_item];
+
+  GURL URL = net::GURLWithNSURL(wk_item.URL);
+  if (IsPlaceholderUrl(URL)) {
+    // No need to create navigation context for placeholder back forward
+    // navigations. Future callbacks do not expect that context will exist.
+    return;
+  }
+
+  // This navigation can be an iframe navigation, but it's not possible to
+  // distinguish it from the main frame navigation, so context still has to be
+  // created.
+  std::unique_ptr<web::NavigationContextImpl> context =
+      web::NavigationContextImpl::CreateNavigationContext(
+          self.webStateImpl, URL, hasUserGesture,
+          static_cast<ui::PageTransition>(
+              item->GetTransitionType() |
+              ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK),
+          type == web::NavigationInitiationType::RENDERER_INITIATED);
+  context->SetNavigationItemUniqueID(item->GetUniqueID());
+  if (!navigation) {
+    // goToBackForwardListItem: returns nil for same-document back forward
+    // navigations.
+    context->SetIsSameDocument(true);
+  } else {
+    self.webStateImpl->SetIsLoading(true);
+    _loadPhase = web::LOAD_REQUESTED;
+  }
+
+  web::WKBackForwardListItemHolder* holder =
+      web::WKBackForwardListItemHolder::FromNavigationItem(item);
+  holder->set_navigation_type(WKNavigationTypeBackForward);
+  context->SetIsPost((holder && [holder->http_method() isEqual:@"POST"]) ||
+                     item->HasPostData());
+
+  if (holder) {
+    context->SetMimeType(holder->mime_type());
+  }
+
+  [_navigationStates setContext:std::move(context) forNavigation:navigation];
+  [_navigationStates setState:web::WKNavigationState::REQUESTED
+                forNavigation:navigation];
+}
+
+- (void)takeSnapshotWithRect:(CGRect)rect
+                  completion:(void (^)(UIImage*))completion {
+  if (@available(iOS 11, *)) {
+    if (self.webView) {
+      WKSnapshotConfiguration* configuration =
+          [[WKSnapshotConfiguration alloc] init];
+      configuration.rect = [self.webView convertRect:rect fromView:self.view];
+      __weak CRWWebController* weakSelf = self;
+      [self.webView
+          takeSnapshotWithConfiguration:configuration
+                      completionHandler:^(UIImage* snapshot, NSError* error) {
+                        // Pass nil to the completion block if there is an error
+                        // or if the web view has been removed before the
+                        // snapshot is finished.  |snapshot| can sometimes be
+                        // corrupt if it's sent due to the WKWebView's
+                        // deallocation, so callbacks received after
+                        // |-removeWebView| are ignored to prevent crashing.
+                        if (error || !weakSelf.webView) {
+                          if (error) {
+                            DLOG(ERROR) << "WKWebView snapshot error: "
+                                        << error.description;
+                          }
+                          completion(nil);
+                        } else {
+                          completion(snapshot);
+                        }
+                      }];
+      return;
+    }
+  }
+  dispatch_async(dispatch_get_main_queue(), ^{
+    completion(nil);
+  });
+}
+
+#pragma mark - CRWJSInjectionEvaluator (Public)
+
+- (void)executeJavaScript:(NSString*)script
+        completionHandler:(web::JavaScriptResultBlock)completionHandler {
+  NSString* safeScript = [self scriptByAddingWindowIDCheckForScript:script];
+  web::ExecuteJavaScript(self.webView, safeScript, completionHandler);
+}
+
+- (BOOL)scriptHasBeenInjectedForClass:(Class)injectionManagerClass {
+  return [_injectedScriptManagers containsObject:injectionManagerClass];
+}
+
+- (void)injectScript:(NSString*)script forClass:(Class)JSInjectionManagerClass {
+  DCHECK(script.length);
+  // Script execution is an asynchronous operation which may pass sensitive
+  // data to the page. executeJavaScript:completionHandler makes sure that
+  // receiver page did not change by checking its window id.
+  // |[self.webView executeJavaScript:completionHandler:]| is not used here
+  // because it does not check that page is the same.
+  [self executeJavaScript:script completionHandler:nil];
+  [_injectedScriptManagers addObject:JSInjectionManagerClass];
+}
+
+#pragma mark - CRWSessionControllerDelegate (Public)
+
+- (web::NavigationItemImpl*)pendingItemForSessionController:
+    (CRWSessionController*)sessionController {
+  WKNavigation* navigation =
+      [_navigationStates lastNavigationWithPendingItemInNavigationContext];
+  if (!navigation)
+    return nullptr;
+  return [_navigationStates contextForNavigation:navigation] -> GetItem();
+}
+
+#pragma mark - CRWTouchTrackingDelegate (Public)
+
+- (void)touched:(BOOL)touched {
+  _clickInProgress = touched;
+  if (touched) {
+    self.userInteractionRegistered = YES;
+    _userInteractedWithWebController = YES;
+    if (_isBeingDestroyed)
+      return;
+    const NavigationManagerImpl* navigationManager = self.navigationManagerImpl;
+    GURL mainDocumentURL =
+        navigationManager->GetLastCommittedItem()
+            ? navigationManager->GetLastCommittedItem()->GetURL()
+            : [self currentURL];
+    _lastUserInteraction =
+        std::make_unique<UserInteractionEvent>(mainDocumentURL);
+  }
+}
+
+#pragma mark - ** Private Methods **
+
+#pragma mark -
+
+- (void)setNativeControllerWebUsageEnabled:(BOOL)webUsageEnabled {
+  if ([self.nativeController
+          respondsToSelector:@selector(setWebUsageEnabled:)]) {
+    [self.nativeController setWebUsageEnabled:webUsageEnabled];
+  }
+}
+
+- (BOOL)isSafeBrowsingWarningDisplayedInWebView {
+  // A SafeBrowsing warning is a UIScrollView that is inserted on top of
+  // WKWebView's scroll view. This method uses heuristics to detect this view.
+  // It may break in the future if WebKit's implementation of SafeBrowsing
+  // warnings changes.
+  UIView* containingView = self.webView.scrollView.superview;
+  if (!containingView)
+    return NO;
+
+  UIView* topView = containingView.subviews.lastObject;
+
+  if (topView == self.webView.scrollView)
+    return NO;
+
+  return
+      [topView isKindOfClass:[UIScrollView class]] &&
+      [NSStringFromClass([topView class]) containsString:@"Warning"] &&
+      topView.subviews.count > 0 &&
+      [topView.subviews[0].subviews.lastObject isKindOfClass:[UIButton class]];
+}
+
 - (void)pushStateWithPageURL:(const GURL&)pageURL
                  stateObject:(NSString*)stateObject
                   transition:(ui::PageTransition)transition
               hasUserGesture:(BOOL)hasUserGesture {
   std::unique_ptr<web::NavigationContextImpl> context =
       web::NavigationContextImpl::CreateNavigationContext(
-          _webStateImpl, pageURL, hasUserGesture, transition,
+          self.webStateImpl, pageURL, hasUserGesture, transition,
           /*is_renderer_initiated=*/true);
   context->SetIsSameDocument(true);
-  _webStateImpl->OnNavigationStarted(context.get());
+  self.webStateImpl->OnNavigationStarted(context.get());
   self.navigationManagerImpl->AddPushStateItemIfNecessary(pageURL, stateObject,
                                                           transition);
   context->SetHasCommitted(true);
-  _webStateImpl->OnNavigationFinished(context.get());
+  self.webStateImpl->OnNavigationFinished(context.get());
   self.userInteractionRegistered = NO;
 }
 
@@ -1270,15 +1807,15 @@
                  hasUserGesture:(BOOL)hasUserGesture {
   std::unique_ptr<web::NavigationContextImpl> context =
       web::NavigationContextImpl::CreateNavigationContext(
-          _webStateImpl, pageURL, hasUserGesture,
+          self.webStateImpl, pageURL, hasUserGesture,
           ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
           /*is_renderer_initiated=*/true);
   context->SetIsSameDocument(true);
-  _webStateImpl->OnNavigationStarted(context.get());
+  self.webStateImpl->OnNavigationStarted(context.get());
   self.navigationManagerImpl->UpdateCurrentItemForReplaceState(pageURL,
                                                                stateObject);
   context->SetHasCommitted(true);
-  _webStateImpl->OnNavigationFinished(context.get());
+  self.webStateImpl->OnNavigationFinished(context.get());
 }
 
 - (void)setDocumentURL:(const GURL&)newURL
@@ -1290,10 +1827,10 @@
   if (web::GetWebClient()->IsSlimNavigationManagerEnabled() && context &&
       !context->IsLoadingHtmlString() && !context->IsLoadingErrorPage() &&
       !IsWKInternalUrl(newURL) && !newURL.SchemeIs(url::kAboutScheme) &&
-      _webView) {
+      self.webView) {
     GURL documentOrigin = newURL.GetOrigin();
     web::NavigationItem* committedItem =
-        _webStateImpl->GetNavigationManager()->GetLastCommittedItem();
+        self.webStateImpl->GetNavigationManager()->GetLastCommittedItem();
     GURL committedOrigin =
         committedItem ? committedItem->GetURL().GetOrigin() : GURL::EmptyGURL();
     DCHECK_EQ(documentOrigin, committedOrigin)
@@ -1317,7 +1854,7 @@
     return;
 
   item->SetTitle(base::SysNSStringToUTF16(title));
-  _webStateImpl->OnTitleChanged();
+  self.webStateImpl->OnTitleChanged();
 }
 
 - (BOOL)isCurrentNavigationItemPOST {
@@ -1347,31 +1884,12 @@
 - (BOOL)isBackForwardListItemValid:(WKBackForwardListItem*)item {
   // The current back-forward list item MUST be in the WKWebView's back-forward
   // list to be valid.
-  WKBackForwardList* list = [_webView backForwardList];
+  WKBackForwardList* list = self.webView.backForwardList;
   return list.currentItem == item ||
          [list.forwardList indexOfObject:item] != NSNotFound ||
          [list.backList indexOfObject:item] != NSNotFound;
 }
 
-- (UIView*)view {
-  [self ensureContainerViewCreated];
-  DCHECK(_containerView);
-  return _containerView;
-}
-
-- (id<CRWWebViewNavigationProxy>)webViewNavigationProxy {
-  return static_cast<id<CRWWebViewNavigationProxy>>(self.webView);
-}
-
-- (UIView*)viewForPrinting {
-  // Printing is not supported for native controllers.
-  return _webView;
-}
-
-- (double)loadingProgress {
-  return [_webView estimatedProgress];
-}
-
 - (std::unique_ptr<web::NavigationContextImpl>)
     registerLoadRequestForURL:(const GURL&)URL
        sameDocumentNavigation:(BOOL)sameDocumentNavigation
@@ -1388,9 +1906,9 @@
 
   if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
       navigationType == WKNavigationTypeBackForward &&
-      _webView.backForwardList.currentItem) {
+      self.webView.backForwardList.currentItem) {
     web::NavigationItem* currentItem = [[CRWNavigationItemHolder
-        holderForBackForwardListItem:_webView.backForwardList.currentItem]
+        holderForBackForwardListItem:self.webView.backForwardList.currentItem]
         navigationItem];
     if (currentItem) {
       transition = ui::PageTransitionFromInt(transition |
@@ -1475,7 +1993,7 @@
 
   std::unique_ptr<web::NavigationContextImpl> context =
       web::NavigationContextImpl::CreateNavigationContext(
-          _webStateImpl, requestURL, hasUserGesture, transition,
+          self.webStateImpl, requestURL, hasUserGesture, transition,
           rendererInitiated);
   context->SetPlaceholderNavigation(placeholderNavigation);
 
@@ -1492,7 +2010,7 @@
   context->SetIsSameDocument(sameDocumentNavigation);
 
   if (!IsWKInternalUrl(requestURL) && !placeholderNavigation) {
-    _webStateImpl->SetIsLoading(true);
+    self.webStateImpl->SetIsLoading(true);
 
     // WKBasedNavigationManager triggers HTML load when placeholder navigation
     // finishes.
@@ -1546,48 +2064,6 @@
   }
 }
 
-- (void)loadData:(NSData*)data
-        MIMEType:(NSString*)MIMEType
-          forURL:(const GURL&)URL {
-  web::NavigationItemImpl* item =
-      self.navigationManagerImpl->GetLastCommittedItemImpl();
-  auto navigationContext = web::NavigationContextImpl::CreateNavigationContext(
-      _webStateImpl, URL,
-      /*has_user_gesture=*/true, item->GetTransitionType(),
-      /*is_renderer_initiated=*/false);
-  _loadPhase = web::LOAD_REQUESTED;
-  navigationContext->SetNavigationItemUniqueID(item->GetUniqueID());
-
-  item->SetNavigationInitiationType(
-      web::NavigationInitiationType::BROWSER_INITIATED);
-  // The error_retry_state_machine may still be in the
-  // |kDisplayingWebErrorForFailedNavigation| from the navigation that is being
-  // replaced. As the navigation is now successful, the error can be cleared.
-  item->error_retry_state_machine().SetNoNavigationError();
-  // The load data call will replace the current navigation and the webView URL
-  // of the navigation will be replaced by |URL|. Set the URL of the
-  // navigationItem to keep them synced.
-  // Note: it is possible that the URL in item already match |url|. But item can
-  // also contain a placeholder URL intended to be replaced.
-  item->SetURL(URL);
-  navigationContext->SetMimeType(MIMEType);
-  if (item->GetUserAgentType() == web::UserAgentType::NONE &&
-      web::wk_navigation_util::URLNeedsUserAgentType(URL)) {
-    item->SetUserAgentType(web::UserAgentType::MOBILE);
-  }
-
-  WKNavigation* navigation =
-      [_webView loadData:data
-                       MIMEType:MIMEType
-          characterEncodingName:base::SysUTF8ToNSString(base::kCodepageUTF8)
-                        baseURL:net::NSURLWithGURL(URL)];
-
-  [_navigationStates setContext:std::move(navigationContext)
-                  forNavigation:navigation];
-  [_navigationStates setState:web::WKNavigationState::REQUESTED
-                forNavigation:navigation];
-}
-
 // TODO(crbug.com/788465): Verify that the history state management here are not
 // needed for WKBasedNavigationManagerImpl and delete this method. The
 // OnNavigationItemCommitted() call is likely the only thing that needs to be
@@ -1689,8 +2165,8 @@
   // session history and UI.
   if (!targetURL.is_valid()) {
     [self didFinishWithURL:targetURL loadSuccess:NO context:nullptr];
-    _webStateImpl->SetIsLoading(false);
-    _webStateImpl->OnPageLoaded(targetURL, NO);
+    self.webStateImpl->SetIsLoading(false);
+    self.webStateImpl->OnPageLoaded(targetURL, NO);
     return;
   }
 
@@ -1703,6 +2179,228 @@
   [self loadRequestForCurrentNavigationItem];
 }
 
+- (void)loadRequestForCurrentNavigationItem {
+  DCHECK(self.webView);
+  DCHECK(self.currentNavItem);
+  // If a load is kicked off on a WKWebView with a frame whose size is {0, 0} or
+  // that has a negative dimension for a size, rendering issues occur that
+  // manifest in erroneous scrolling and tap handling (crbug.com/574996,
+  // crbug.com/577793).
+  DCHECK_GT(CGRectGetWidth(self.webView.frame), 0.0);
+  DCHECK_GT(CGRectGetHeight(self.webView.frame), 0.0);
+
+  // If the current item uses a different user agent from that is currently used
+  // in the web view, update |customUserAgent| property, which will be used by
+  // the next request sent by this web view.
+  web::UserAgentType itemUserAgentType =
+      self.currentNavItem->GetUserAgentType();
+  if (itemUserAgentType != web::UserAgentType::NONE) {
+    NSString* userAgentString = base::SysUTF8ToNSString(
+        web::GetWebClient()->GetUserAgent(itemUserAgentType));
+    if (![self.webView.customUserAgent isEqualToString:userAgentString]) {
+      self.webView.customUserAgent = userAgentString;
+    }
+  }
+
+  web::WKBackForwardListItemHolder* holder =
+      [self currentBackForwardListItemHolder];
+  BOOL repostedForm =
+      [holder->http_method() isEqual:@"POST"] &&
+      (holder->navigation_type() == WKNavigationTypeFormResubmitted ||
+       holder->navigation_type() == WKNavigationTypeFormSubmitted);
+  web::NavigationItemImpl* currentItem = self.currentNavItem;
+  NSData* POSTData = currentItem->GetPostData();
+  NSMutableURLRequest* request = [self requestForCurrentNavigationItem];
+
+  BOOL sameDocumentNavigation = currentItem->IsCreatedFromPushState() ||
+                                currentItem->IsCreatedFromHashChange();
+
+  if (holder->back_forward_list_item()) {
+    // Check if holder's WKBackForwardListItem still correctly represents
+    // navigation item. With LegacyNavigationManager, replaceState operation
+    // creates a new navigation item, leaving the old item committed. That
+    // old committed item will be associated with WKBackForwardListItem whose
+    // state was replaced. So old item won't have correct WKBackForwardListItem.
+    if (net::GURLWithNSURL(holder->back_forward_list_item().URL) !=
+        currentItem->GetURL()) {
+      // The state was replaced for this item. The item should not be a part of
+      // committed items, but it's too late to remove the item. Cleaup
+      // WKBackForwardListItem and mark item with "state replaced" flag.
+      currentItem->SetHasStateBeenReplaced(true);
+      holder->set_back_forward_list_item(nil);
+    }
+  }
+
+  // If the request has POST data and is not a repost form, configure and
+  // run the POST request.
+  if (POSTData.length && !repostedForm) {
+    [request setHTTPMethod:@"POST"];
+    [request setHTTPBody:POSTData];
+    [request setAllHTTPHeaderFields:self.currentHTTPHeaders];
+    // As of iOS 11, WKWebView supports requests with POST data, so the
+    // Javascript POST workaround only needs to be used if the OS version is
+    // less than iOS 11.
+    // TODO(crbug.com/740987): Remove POST workaround once iOS 10 is dropped.
+    if (!base::ios::IsRunningOnIOS11OrLater()) {
+      GURL navigationURL =
+          currentItem ? currentItem->GetURL() : GURL::EmptyGURL();
+      std::unique_ptr<web::NavigationContextImpl> navigationContext =
+          [self registerLoadRequestForURL:navigationURL
+                                 referrer:self.currentNavItemReferrer
+                               transition:self.currentTransition
+                   sameDocumentNavigation:sameDocumentNavigation
+                           hasUserGesture:YES
+                        rendererInitiated:NO
+                    placeholderNavigation:NO];
+      WKNavigation* navigation = [self loadPOSTRequest:request];
+      [_navigationStates setContext:std::move(navigationContext)
+                      forNavigation:navigation];
+      [_navigationStates setState:web::WKNavigationState::REQUESTED
+                    forNavigation:navigation];
+      return;
+    }
+  }
+
+  ProceduralBlock defaultNavigationBlock = ^{
+    web::NavigationItem* item = self.currentNavItem;
+    GURL navigationURL = item ? item->GetURL() : GURL::EmptyGURL();
+    GURL contextURL = IsPlaceholderUrl(navigationURL)
+                          ? ExtractUrlFromPlaceholderUrl(navigationURL)
+                          : navigationURL;
+    std::unique_ptr<web::NavigationContextImpl> navigationContext =
+        [self registerLoadRequestForURL:contextURL
+                               referrer:self.currentNavItemReferrer
+                             transition:self.currentTransition
+                 sameDocumentNavigation:sameDocumentNavigation
+                         hasUserGesture:YES
+                      rendererInitiated:NO
+                  placeholderNavigation:IsPlaceholderUrl(navigationURL)];
+
+    WKNavigation* navigation = nil;
+    GURL virtualURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
+    if (navigationURL.SchemeIsFile() &&
+        web::GetWebClient()->IsAppSpecificURL(virtualURL)) {
+      // file:// URL navigations are allowed for app-specific URLs, which
+      // already have elevated privileges.
+      NSURL* navigationNSURL = net::NSURLWithGURL(navigationURL);
+      navigation = [self.webView loadFileURL:navigationNSURL
+                     allowingReadAccessToURL:navigationNSURL];
+    } else {
+      navigation = [self.webView loadRequest:request];
+    }
+    [_navigationStates setState:web::WKNavigationState::REQUESTED
+                  forNavigation:navigation];
+    [_navigationStates setContext:std::move(navigationContext)
+                    forNavigation:navigation];
+    [self reportBackForwardNavigationTypeForFastNavigation:NO];
+  };
+
+  // When navigating via WKBackForwardListItem to pages created or updated by
+  // calls to pushState() and replaceState(), sometimes web_bundle.js is not
+  // injected correctly.  This means that calling window.history navigation
+  // functions will invoke WKWebView's non-overridden implementations, causing a
+  // mismatch between the WKBackForwardList and NavigationManager.
+  // TODO(crbug.com/659816): Figure out how to prevent web_bundle.js injection
+  // flake.
+  if (currentItem->HasStateBeenReplaced() ||
+      currentItem->IsCreatedFromPushState()) {
+    defaultNavigationBlock();
+    return;
+  }
+
+  // If there is no corresponding WKBackForwardListItem, or the item is not in
+  // the current WKWebView's back-forward list, navigating using WKWebView API
+  // is not possible. In this case, fall back to the default navigation
+  // mechanism.
+  if (!holder->back_forward_list_item() ||
+      ![self isBackForwardListItemValid:holder->back_forward_list_item()]) {
+    defaultNavigationBlock();
+    return;
+  }
+
+  ProceduralBlock webViewNavigationBlock = ^{
+    // If the current navigation URL is the same as the URL of the visible
+    // page, that means the user requested a reload. |goToBackForwardListItem|
+    // will be a no-op when it is passed the current back forward list item,
+    // so |reload| must be explicitly called.
+    web::NavigationItem* item = self.currentNavItem;
+    GURL navigationURL = item ? item->GetURL() : GURL::EmptyGURL();
+    std::unique_ptr<web::NavigationContextImpl> navigationContext =
+        [self registerLoadRequestForURL:navigationURL
+                               referrer:self.currentNavItemReferrer
+                             transition:self.currentTransition
+                 sameDocumentNavigation:sameDocumentNavigation
+                         hasUserGesture:YES
+                      rendererInitiated:NO
+                  placeholderNavigation:NO];
+    WKNavigation* navigation = nil;
+    if (navigationURL == net::GURLWithNSURL(self.webView.URL)) {
+      navigation = [self.webView reload];
+    } else {
+      // |didCommitNavigation:| may not be called for fast navigation, so update
+      // the navigation type now as it is already known.
+      navigationContext->SetWKNavigationType(WKNavigationTypeBackForward);
+      navigationContext->SetMimeType(holder->mime_type());
+      holder->set_navigation_type(WKNavigationTypeBackForward);
+      navigation = [self.webView
+          goToBackForwardListItem:holder->back_forward_list_item()];
+      [self reportBackForwardNavigationTypeForFastNavigation:YES];
+    }
+    [_navigationStates setState:web::WKNavigationState::REQUESTED
+                  forNavigation:navigation];
+    [_navigationStates setContext:std::move(navigationContext)
+                    forNavigation:navigation];
+  };
+
+  // If the request is not a form submission or resubmission, or the user
+  // doesn't need to confirm the load, then continue right away.
+
+  if (!repostedForm || currentItem->ShouldSkipRepostFormConfirmation()) {
+    webViewNavigationBlock();
+    return;
+  }
+
+  // If the request is form submission or resubmission, then prompt the
+  // user before proceeding.
+  DCHECK(repostedForm);
+  DCHECK(!web::GetWebClient()->IsSlimNavigationManagerEnabled());
+  self.webStateImpl->ShowRepostFormWarningDialog(
+      base::BindOnce(^(bool shouldContinue) {
+        if (_isBeingDestroyed)
+          return;
+
+        if (shouldContinue)
+          webViewNavigationBlock();
+        else
+          [self stopLoading];
+      }));
+}
+
+- (void)reportBackForwardNavigationTypeForFastNavigation:(BOOL)isFast {
+  // TODO(crbug.com/665189): Use NavigationManager::GetPendingItemIndex() once
+  // it returns correct result.
+  int pendingIndex = self.sessionController.pendingItemIndex;
+  if (pendingIndex == -1) {
+    // Pending navigation is not a back forward navigation.
+    return;
+  }
+
+  BOOL isBack =
+      pendingIndex < self.navigationManagerImpl->GetLastCommittedItemIndex();
+  BackForwardNavigationType type = BackForwardNavigationType::FAST_BACK;
+  if (isBack) {
+    type = isFast ? BackForwardNavigationType::FAST_BACK
+                  : BackForwardNavigationType::SLOW_BACK;
+  } else {
+    type = isFast ? BackForwardNavigationType::FAST_FORWARD
+                  : BackForwardNavigationType::SLOW_FORWARD;
+  }
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "Navigation.IOSWKWebViewSlowFastBackForward", type,
+      BackForwardNavigationType::BACK_FORWARD_NAVIGATION_TYPE_COUNT);
+}
+
 - (void)updatePendingNavigationInfoFromNavigationAction:
     (WKNavigationAction*)action {
   if (action.targetFrame.mainFrame) {
@@ -1767,6 +2465,26 @@
   return request;
 }
 
+- (web::NavigationContextImpl*)contextForPendingMainFrameNavigationWithURL:
+    (const GURL&)URL {
+  // Here the enumeration variable |navigation| is __strong to allow setting it
+  // to nil.
+  for (__strong id navigation in [_navigationStates pendingNavigations]) {
+    if (navigation == [NSNull null]) {
+      // null is a valid navigation object passed to WKNavigationDelegate
+      // callbacks and represents window opening action.
+      navigation = nil;
+    }
+
+    web::NavigationContextImpl* context =
+        [_navigationStates contextForNavigation:navigation];
+    if (context && context->GetUrl() == URL) {
+      return context;
+    }
+  }
+  return nullptr;
+}
+
 - (web::WKBackForwardListItemHolder*)currentBackForwardListItemHolder {
   web::NavigationItem* item = self.currentNavItem;
   DCHECK(item);
@@ -1795,7 +2513,7 @@
   WKNavigationType navigationType =
       _pendingNavigationInfo ? [_pendingNavigationInfo navigationType]
                              : WKNavigationTypeOther;
-  holder->set_back_forward_list_item([_webView backForwardList].currentItem);
+  holder->set_back_forward_list_item(self.webView.backForwardList.currentItem);
   holder->set_navigation_type(navigationType);
   holder->set_http_method([_pendingNavigationInfo HTTPMethod]);
 
@@ -1828,16 +2546,16 @@
   NSString* failing_url = error.userInfo[NSURLErrorFailingURLStringErrorKey];
   NSString* errorHTML = nil;
   web::GetWebClient()->PrepareErrorPage(
-      _webStateImpl, GURL(base::SysNSStringToUTF8(failing_url)), error,
-      context->IsPost(), _webStateImpl->GetBrowserState()->IsOffTheRecord(),
+      self.webStateImpl, GURL(base::SysNSStringToUTF8(failing_url)), error,
+      context->IsPost(), self.webStateImpl->GetBrowserState()->IsOffTheRecord(),
       &errorHTML);
 
   WKNavigation* navigation =
-      [_webView loadHTMLString:errorHTML
-                       baseURL:net::NSURLWithGURL(currentURL)];
+      [self.webView loadHTMLString:errorHTML
+                           baseURL:net::NSURLWithGURL(currentURL)];
 
   auto loadHTMLContext = web::NavigationContextImpl::CreateNavigationContext(
-      _webStateImpl, currentURL,
+      self.webStateImpl, currentURL,
       /*has_user_gesture=*/false, ui::PAGE_TRANSITION_FIRST,
       /*is_renderer_initiated=*/false);
   loadHTMLContext->SetLoadingErrorPage(true);
@@ -1866,12 +2584,12 @@
     context->SetUrl(item->GetURL());
     context->SetPlaceholderNavigation(false);
     context->SetHasCommitted(true);
-    _webStateImpl->OnNavigationFinished(context);
+    self.webStateImpl->OnNavigationFinished(context);
   }
 
   [self loadCompleteWithSuccess:NO forContext:context];
-  _webStateImpl->SetIsLoading(false);
-  _webStateImpl->OnPageLoaded(currentURL, NO);
+  self.webStateImpl->SetIsLoading(false);
+  self.webStateImpl->OnPageLoaded(currentURL, NO);
 }
 
 // Loads the current URL in a native controller if using the legacy navigation
@@ -1929,11 +2647,11 @@
                     rendererInitiated:rendererInitiated
                 placeholderNavigation:NO];
 
-  _webStateImpl->OnNavigationStarted(context.get());
+  self.webStateImpl->OnNavigationStarted(context.get());
   [self didStartLoading];
   self.navigationManagerImpl->CommitPendingItem();
   context->SetHasCommitted(true);
-  _webStateImpl->OnNavigationFinished(context.get());
+  self.webStateImpl->OnNavigationFinished(context.get());
 
   if (item && web::GetWebClient()->IsAppSpecificURL(item->GetURL())) {
     // Report the successful navigation to the ErrorRetryStateMachine.
@@ -1963,7 +2681,7 @@
 
   NSURLRequest* request =
       [NSURLRequest requestWithURL:net::NSURLWithGURL(placeholderURL)];
-  WKNavigation* navigation = [_webView loadRequest:request];
+  WKNavigation* navigation = [self.webView loadRequest:request];
   [_navigationStates setState:web::WKNavigationState::REQUESTED
                 forNavigation:navigation];
   std::unique_ptr<web::NavigationContextImpl> navigationContext;
@@ -2007,7 +2725,7 @@
       break;
 
     case web::ErrorRetryCommand::kReload:
-      [_webView reload];
+      [self.webView reload];
       break;
 
     case web::ErrorRetryCommand::kRewriteWebViewURL: {
@@ -2018,8 +2736,8 @@
                         rendererInitiated:context->IsRendererInitiated()
                     placeholderNavigation:NO];
       WKNavigation* navigation =
-          [_webView loadHTMLString:@""
-                           baseURL:net::NSURLWithGURL(item->GetURL())];
+          [self.webView loadHTMLString:@""
+                               baseURL:net::NSURLWithGURL(item->GetURL())];
       navigationContext->SetError(context->GetError());
       navigationContext->SetIsPost(context->IsPost());
       [_navigationStates setContext:std::move(navigationContext)
@@ -2031,89 +2749,6 @@
   }
 }
 
-- (void)loadCurrentURLWithRendererInitiatedNavigation:(BOOL)rendererInitiated {
-  // If the content view doesn't exist, the tab has either been evicted, or
-  // never displayed. Bail, and let the URL be loaded when the tab is shown.
-  if (!_containerView)
-    return;
-
-  // WKBasedNavigationManagerImpl needs WKWebView to load native views, but
-  // WKWebView cannot be created while web usage is disabled to avoid breaking
-  // clearing browser data. Bail now and let the URL be loaded when web
-  // usage is enabled again. This can happen when purging web pages when an
-  // interstitial is presented over a native view. See https://crbug.com/865985
-  // for details.
-  if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-      !_webUsageEnabled)
-    return;
-
-  _currentURLLoadWasTrigerred = YES;
-
-  // Reset current WebUI if one exists.
-  [self clearWebUI];
-
-  // Abort any outstanding page load. This ensures the delegate gets informed
-  // about the outgoing page, and further messages from the page are suppressed.
-  if (_loadPhase != web::PAGE_LOADED)
-    [self abortLoad];
-
-  DCHECK(!_isHalted);
-  _webStateImpl->ClearTransientContent();
-
-  web::NavigationItem* item = self.currentNavItem;
-  const GURL currentURL = item ? item->GetURL() : GURL::EmptyGURL();
-  const bool isCurrentURLAppSpecific =
-      web::GetWebClient()->IsAppSpecificURL(currentURL);
-  // If it's a chrome URL, but not a native one, create the WebUI instance.
-  if (isCurrentURLAppSpecific &&
-      ![_nativeProvider hasControllerForURL:currentURL]) {
-    if (!(item->GetTransitionType() & ui::PAGE_TRANSITION_TYPED ||
-          item->GetTransitionType() & ui::PAGE_TRANSITION_AUTO_BOOKMARK) &&
-        self.hasOpener) {
-      // WebUI URLs can not be opened by DOM to prevent cross-site scripting as
-      // they have increased power. WebUI URLs may only be opened when the user
-      // types in the URL or use bookmarks.
-      self.navigationManagerImpl->DiscardNonCommittedItems();
-      return;
-    } else {
-      [self createWebUIForURL:currentURL];
-    }
-  }
-
-  // Loading a new url, must check here if it's a native chrome URL and
-  // replace the appropriate view if so, or transition back to a web view from
-  // a native view.
-  if ([self shouldLoadURLInNativeView:currentURL]) {
-    [self loadCurrentURLInNativeViewWithRendererInitiatedNavigation:
-              rendererInitiated];
-  } else if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-             isCurrentURLAppSpecific && _webStateImpl->HasWebUI() &&
-             !web::features::WebUISchemeHandlingEnabled()) {
-    [self loadPlaceholderInWebViewForURL:currentURL
-                       rendererInitiated:rendererInitiated
-                              forContext:nullptr];
-  } else {
-    [self loadCurrentURLInWebView];
-  }
-}
-
-- (void)loadCurrentURLIfNecessary {
-  if (_webProcessCrashed) {
-    [self loadCurrentURLWithRendererInitiatedNavigation:NO];
-  } else if (!_currentURLLoadWasTrigerred) {
-    [self ensureContainerViewCreated];
-
-    // This method reloads last committed item, so make than item also pending.
-    self.sessionController.pendingItemIndex =
-        self.sessionController.lastCommittedItemIndex;
-
-    // TODO(crbug.com/796608): end the practice of calling |loadCurrentURL|
-    // when it is possible there is no current URL. If the call performs
-    // necessary initialization, break that out.
-    [self loadCurrentURLWithRendererInitiatedNavigation:NO];
-  }
-}
-
 - (GURL)webURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel {
   DCHECK(trustLevel);
   *trustLevel = web::URLVerificationTrustLevel::kAbsolute;
@@ -2127,77 +2762,12 @@
 - (BOOL)shouldLoadURLInNativeView:(const GURL&)url {
   // App-specific URLs that don't require WebUI are loaded in native views.
   return web::GetWebClient()->IsAppSpecificURL(url) &&
-         !_webStateImpl->HasWebUI() &&
+         !self.webStateImpl->HasWebUI() &&
          [_nativeProvider hasControllerForURL:url];
 }
 
-- (void)reloadWithRendererInitiatedNavigation:(BOOL)rendererInitiated {
-  // Clear last user interaction.
-  // TODO(crbug.com/546337): Move to after the load commits, in the subclass
-  // implementation. This will be inaccurate if the reload fails or is
-  // cancelled.
-  _lastUserInteraction = nullptr;
-  base::RecordAction(base::UserMetricsAction("Reload"));
-  GURL URL = self.currentNavItem->GetURL();
-  if ([self shouldLoadURLInNativeView:URL]) {
-    std::unique_ptr<web::NavigationContextImpl> navigationContext = [self
-        registerLoadRequestForURL:URL
-                         referrer:self.currentNavItemReferrer
-                       transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
-           sameDocumentNavigation:NO
-                   hasUserGesture:YES
-                rendererInitiated:rendererInitiated
-            placeholderNavigation:NO];
-    _webStateImpl->OnNavigationStarted(navigationContext.get());
-    [self didStartLoading];
-    self.navigationManagerImpl->CommitPendingItem(
-        navigationContext->ReleaseItem());
-    [self.nativeController reload];
-    navigationContext->SetHasCommitted(true);
-    _webStateImpl->OnNavigationFinished(navigationContext.get());
-    [self loadCompleteWithSuccess:YES forContext:nullptr];
-  } else {
-    web::NavigationItem* transientItem =
-        self.navigationManagerImpl->GetTransientItem();
-    if (transientItem) {
-      // If there's a transient item, a reload is considered a new navigation to
-      // the transient item's URL (as on other platforms).
-      NavigationManager::WebLoadParams reloadParams(transientItem->GetURL());
-      reloadParams.transition_type = ui::PAGE_TRANSITION_RELOAD;
-      reloadParams.extra_headers =
-          [transientItem->GetHttpRequestHeaders() copy];
-      self.webState->GetNavigationManager()->LoadURLWithParams(reloadParams);
-    } else {
-      self.currentNavItem->SetTransitionType(
-          ui::PageTransition::PAGE_TRANSITION_RELOAD);
-      if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-          !web::GetWebClient()->IsAppSpecificURL(
-              net::GURLWithNSURL(_webView.URL))) {
-        // New navigation manager can delegate directly to WKWebView to reload
-        // for non-app-specific URLs. The necessary navigation states will be
-        // updated in WKNavigationDelegate callbacks.
-        WKNavigation* navigation = [_webView reload];
-        [_navigationStates setState:web::WKNavigationState::REQUESTED
-                      forNavigation:navigation];
-        std::unique_ptr<web::NavigationContextImpl> navigationContext = [self
-            registerLoadRequestForURL:URL
-                             referrer:self.currentNavItemReferrer
-                           transition:ui::PageTransition::PAGE_TRANSITION_RELOAD
-               sameDocumentNavigation:NO
-                       hasUserGesture:YES
-                    rendererInitiated:rendererInitiated
-                placeholderNavigation:NO];
-        [_navigationStates setContext:std::move(navigationContext)
-                        forNavigation:navigation];
-      } else {
-        [self loadCurrentURLWithRendererInitiatedNavigation:rendererInitiated];
-      }
-    }
-  }
-}
-
 - (void)abortLoad {
-  [_webView stopLoading];
+  [self.webView stopLoading];
   [_pendingNavigationInfo setCancelled:YES];
   _certVerificationErrors->Clear();
   [self loadCancelled];
@@ -2209,13 +2779,13 @@
   if (_loadPhase != web::PAGE_LOADED) {
     _loadPhase = web::PAGE_LOADED;
     if (!_isHalted) {
-      _webStateImpl->SetIsLoading(false);
+      self.webStateImpl->SetIsLoading(false);
     }
   }
 }
 
 - (BOOL)contentIsImage {
-  if (!_webView)
+  if (!self.webView)
     return NO;
 
   const std::string image = "image";
@@ -2316,7 +2886,7 @@
   if (context && IsRestoreSessionUrl(context->GetUrl()))
     return;
 
-  if (IsRestoreSessionUrl(net::GURLWithNSURL(_webView.URL)))
+  if (IsRestoreSessionUrl(net::GURLWithNSURL(self.webView.URL)))
     return;
 
   if (context && context->IsLoadingErrorPage())
@@ -2328,8 +2898,8 @@
     return;
   }
 
-  _webStateImpl->SetIsLoading(false);
-  _webStateImpl->OnPageLoaded(currentURL, YES);
+  self.webStateImpl->SetIsLoading(false);
+  self.webStateImpl->OnPageLoaded(currentURL, YES);
 }
 
 - (void)rendererInitiatedGoDelta:(int)delta
@@ -2350,26 +2920,6 @@
   }
 }
 
-- (void)addGestureRecognizerToWebView:(UIGestureRecognizer*)recognizer {
-  if ([_gestureRecognizers containsObject:recognizer])
-    return;
-
-  [_webView addGestureRecognizer:recognizer];
-  [_gestureRecognizers addObject:recognizer];
-}
-
-- (void)removeGestureRecognizerFromWebView:(UIGestureRecognizer*)recognizer {
-  if (![_gestureRecognizers containsObject:recognizer])
-    return;
-
-  [_webView removeGestureRecognizer:recognizer];
-  [_gestureRecognizers removeObject:recognizer];
-}
-
-- (CRWJSInjectionReceiver*)jsInjectionReceiver {
-  return _jsInjectionReceiver;
-}
-
 - (BOOL)shouldClosePageOnNativeApplicationLoad {
   // The page should be closed if it was initiated by the DOM and there has been
   // no user interaction with the page since the web view was created, or if
@@ -2381,20 +2931,6 @@
   return rendererInitiatedWithoutInteraction || noNavigationItems;
 }
 
-- (web::UserAgentType)userAgentType {
-  web::NavigationItem* item = self.currentNavItem;
-  return item ? item->GetUserAgentType() : web::UserAgentType::MOBILE;
-}
-
-- (web::MojoFacade*)mojoFacade {
-  if (!_mojoFacade) {
-    service_manager::mojom::InterfaceProvider* interfaceProvider =
-        _webStateImpl->GetWebStateInterfaceProvider();
-    _mojoFacade.reset(new web::MojoFacade(interfaceProvider, self));
-  }
-  return _mojoFacade.get();
-}
-
 - (void)updateDesktopUserAgentForItem:(web::NavigationItem*)item
                 previousUserAgentType:(web::UserAgentType)userAgentType {
   if (!item)
@@ -2414,43 +2950,7 @@
     self.navigationManagerImpl->DiscardNonCommittedItems();
 }
 
-- (void)takeSnapshotWithRect:(CGRect)rect
-                  completion:(void (^)(UIImage*))completion {
-  if (@available(iOS 11, *)) {
-    if (_webView) {
-      WKSnapshotConfiguration* configuration =
-          [[WKSnapshotConfiguration alloc] init];
-      configuration.rect = [_webView convertRect:rect fromView:self.view];
-      __weak CRWWebController* weakSelf = self;
-      [_webView
-          takeSnapshotWithConfiguration:configuration
-                      completionHandler:^(UIImage* snapshot, NSError* error) {
-                        // Pass nil to the completion block if there is an error
-                        // or if the web view has been removed before the
-                        // snapshot is finished.  |snapshot| can sometimes be
-                        // corrupt if it's sent due to the WKWebView's
-                        // deallocation, so callbacks received after
-                        // |-removeWebView| are ignored to prevent crashing.
-                        if (error || !weakSelf.webView) {
-                          if (error) {
-                            DLOG(ERROR) << "WKWebView snapshot error: "
-                                        << error.description;
-                          }
-                          completion(nil);
-                        } else {
-                          completion(snapshot);
-                        }
-                      }];
-      return;
-    }
-  }
-  dispatch_async(dispatch_get_main_queue(), ^{
-    completion(nil);
-  });
-}
-
-#pragma mark -
-#pragma mark CRWWebControllerContainerViewDelegate
+#pragma mark - CRWWebControllerContainerViewDelegate
 
 - (CRWWebViewProxyImpl*)contentViewProxyForContainerView:
         (CRWWebControllerContainerView*)containerView {
@@ -2462,53 +2962,7 @@
   return [self.nativeProvider nativeContentInsetForWebState:self.webState];
 }
 
-#pragma mark -
-#pragma mark CRWJSInjectionEvaluator Methods
-
-- (void)executeJavaScript:(NSString*)script
-        completionHandler:(web::JavaScriptResultBlock)completionHandler {
-  NSString* safeScript = [self scriptByAddingWindowIDCheckForScript:script];
-  web::ExecuteJavaScript(_webView, safeScript, completionHandler);
-}
-
-- (BOOL)scriptHasBeenInjectedForClass:(Class)injectionManagerClass {
-  return [_injectedScriptManagers containsObject:injectionManagerClass];
-}
-
-- (void)injectScript:(NSString*)script forClass:(Class)JSInjectionManagerClass {
-  DCHECK(script.length);
-  // Script execution is an asynchronous operation which may pass sensitive
-  // data to the page. executeJavaScript:completionHandler makes sure that
-  // receiver page did not change by checking its window id.
-  // |[_webView executeJavaScript:completionHandler:]| is not used here because
-  // it does not check that page is the same.
-  [self executeJavaScript:script completionHandler:nil];
-  [_injectedScriptManagers addObject:JSInjectionManagerClass];
-}
-
-#pragma mark -
-
-- (void)executeUserJavaScript:(NSString*)script
-            completionHandler:(web::JavaScriptResultBlock)completion {
-  // For security reasons, executing JavaScript on pages with app-specific URLs
-  // is not allowed, because those pages may have elevated privileges.
-  GURL lastCommittedURL = self.webState->GetLastCommittedURL();
-  if (web::GetWebClient()->IsAppSpecificURL(lastCommittedURL)) {
-    if (completion) {
-      dispatch_async(dispatch_get_main_queue(), ^{
-        NSError* error = [[NSError alloc]
-            initWithDomain:web::kJSEvaluationErrorDomain
-                      code:web::JS_EVALUATION_ERROR_CODE_NO_WEB_VIEW
-                  userInfo:nil];
-        completion(nil, error);
-      });
-    }
-    return;
-  }
-
-  [self touched:YES];
-  [self executeJavaScript:script completionHandler:completion];
-}
+#pragma mark - CRWJSInjectionEvaluator helper methods (Private)
 
 - (BOOL)respondToMessage:(base::DictionaryValue*)message
        userIsInteracting:(BOOL)userIsInteracting
@@ -2647,13 +3101,22 @@
   }
   return [self respondToMessage:command
               userIsInteracting:[self userIsInteracting]
-                      originURL:net::GURLWithNSURL([_webView URL])
+                      originURL:net::GURLWithNSURL(self.webView.URL)
                     isMainFrame:scriptMessage.frameInfo.mainFrame
                     senderFrame:senderFrame];
 }
 
-#pragma mark -
-#pragma mark Web frames management
+- (BOOL)userIsInteracting {
+  // If page transfer started after last click, user is deemed to be no longer
+  // interacting.
+  if (!_lastUserInteraction ||
+      _lastTransferTimeInSeconds > _lastUserInteraction->time) {
+    return NO;
+  }
+  return [self userClickedRecently];
+}
+
+#pragma mark - Web frames management
 
 - (void)frameBecameAvailableWithMessage:(WKScriptMessage*)message {
   // Validate all expected message components because any frame could falsify
@@ -2698,7 +3161,8 @@
     }
 
     framesManager->AddFrame(std::move(newFrame));
-    _webStateImpl->OnWebFrameAvailable(framesManager->GetFrameWithId(frameID));
+    self.webStateImpl->OnWebFrameAvailable(
+        framesManager->GetFrameWithId(frameID));
   }
 }
 
@@ -2712,7 +3176,7 @@
       web::WebFramesManagerImpl::FromWebState([self webState]);
 
   if (framesManager->GetFrameWithId(frameID)) {
-    _webStateImpl->OnWebFrameUnavailable(
+    self.webStateImpl->OnWebFrameUnavailable(
         framesManager->GetFrameWithId(frameID));
     framesManager->RemoveFrameWithId(frameID);
   }
@@ -2722,20 +3186,19 @@
   web::WebFramesManagerImpl* framesManager =
       web::WebFramesManagerImpl::FromWebState([self webState]);
   for (auto* frame : framesManager->GetAllWebFrames()) {
-    _webStateImpl->OnWebFrameUnavailable(frame);
+    self.webStateImpl->OnWebFrameUnavailable(frame);
   }
   framesManager->RemoveAllWebFrames();
 }
 
-#pragma mark -
-#pragma mark JavaScript message handlers
+#pragma mark - JavaScript message handlers
 
 - (BOOL)handleChromeSendMessage:(base::DictionaryValue*)message
                         context:(NSDictionary*)context {
   // Chrome message are only handled if sent from the main frame.
   if (![context[kIsMainFrame] boolValue])
     return NO;
-  if (_webStateImpl->HasWebUI()) {
+  if (self.webStateImpl->HasWebUI()) {
     const GURL currentURL([self currentURL]);
     if (web::GetWebClient()->IsAppSpecificURL(currentURL)) {
       std::string messageContent;
@@ -2750,11 +3213,11 @@
       }
       // WebFrame messaging is not supported in WebUI (as window.isSecureContext
       // is false. Pass nullptr as sender_frame.
-      _webStateImpl->OnScriptCommandReceived(
+      self.webStateImpl->OnScriptCommandReceived(
           messageContent, *message, currentURL, context[kUserIsInteractingKey],
           [context[kIsMainFrame] boolValue], nullptr);
-      _webStateImpl->ProcessWebUIMessage(currentURL, messageContent,
-                                         *arguments);
+      self.webStateImpl->ProcessWebUIMessage(currentURL, messageContent,
+                                             *arguments);
       return YES;
     }
   }
@@ -2780,7 +3243,7 @@
     return NO;
 
   if (!URLs.empty())
-    _webStateImpl->OnFaviconUrlUpdated(URLs);
+    self.webStateImpl->OnFaviconUrlUpdated(URLs);
   return YES;
 }
 
@@ -3058,23 +3521,6 @@
   _pageHasZoomed = NO;
 }
 
-- (void)wasShown {
-  self.visible = YES;
-  if ([self.nativeController respondsToSelector:@selector(wasShown)]) {
-    [self.nativeController wasShown];
-  }
-}
-
-- (void)wasHidden {
-  self.visible = NO;
-  if (_isHalted)
-    return;
-  [self recordStateInHistory];
-  if ([self.nativeController respondsToSelector:@selector(wasHidden)]) {
-    [self.nativeController wasHidden];
-  }
-}
-
 + (BOOL)webControllerCanShow:(const GURL&)url {
   return web::UrlHasWebScheme(url) ||
          web::GetWebClient()->IsAppSpecificURL(url) ||
@@ -3082,16 +3528,6 @@
          url.SchemeIs(url::kBlobScheme);
 }
 
-- (void)setUserInteractionRegistered:(BOOL)flag {
-  _userInteractionRegistered = flag;
-  if (flag)
-    _interactionRegisteredSinceLastURLChange = YES;
-}
-
-- (BOOL)userInteractionRegistered {
-  return _userInteractionRegistered;
-}
-
 - (void)cachePOSTDataForRequest:(NSURLRequest*)request
                inNavigationItem:(web::NavigationItemImpl*)item {
   NSUInteger maxPOSTDataSizeInBytes = 4096;
@@ -3140,7 +3576,7 @@
   GURL requestURL = net::GURLWithNSURL(action.request.URL);
   DCHECK(web::GetWebClient()->IsAppSpecificURL(requestURL));
   if (web::GetWebClient()->IsAppSpecificURL(
-          _webStateImpl->GetLastCommittedURL())) {
+          self.webStateImpl->GetLastCommittedURL())) {
     // Last committed page is also app specific and navigation should be
     // allowed.
     return YES;
@@ -3224,7 +3660,7 @@
         // Universal Link and called webView:didFailProvisionalNavigation:.
         self.navigationManagerImpl->DiscardNonCommittedItems();
       }
-      _webStateImpl->SetIsLoading(false);
+      self.webStateImpl->SetIsLoading(false);
       return;
     }
   }
@@ -3236,7 +3672,7 @@
     // status for error pages.
     if (!lastCommittedItem->GetSSL().Equals(web::SSLStatus())) {
       lastCommittedItem->GetSSL() = web::SSLStatus();
-      _webStateImpl->DidChangeVisibleSecurityState();
+      self.webStateImpl->DidChangeVisibleSecurityState();
     }
   }
 
@@ -3249,10 +3685,10 @@
     web::ErrorRetryCommand command = web::ErrorRetryCommand::kDoNothing;
     if (provisionalLoad) {
       command = item->error_retry_state_machine().DidFailProvisionalNavigation(
-          net::GURLWithNSURL(_webView.URL), errorURL);
+          net::GURLWithNSURL(self.webView.URL), errorURL);
     } else {
       command = item->error_retry_state_machine().DidFailNavigation(
-          net::GURLWithNSURL(_webView.URL), errorURL);
+          net::GURLWithNSURL(self.webView.URL), errorURL);
     }
     [self handleErrorRetryCommand:command
                    navigationItem:item
@@ -3287,7 +3723,7 @@
     }
 
     if (provisionalLoad) {
-      _webStateImpl->OnNavigationFinished(navigationContext);
+      self.webStateImpl->OnNavigationFinished(navigationContext);
     }
   }
 }
@@ -3319,7 +3755,7 @@
   }
 
   BOOL mainFrame = WKResponse.forMainFrame;
-  if (!_webStateImpl->ShouldAllowResponse(WKResponse.response, mainFrame)) {
+  if (!self.webStateImpl->ShouldAllowResponse(WKResponse.response, mainFrame)) {
     return NO;
   }
 
@@ -3355,7 +3791,7 @@
         [self contextForPendingMainFrameNavigationWithURL:responseURL];
     context->SetIsDownload(true);
     // Navigation callbacks can only be called for the main frame.
-    _webStateImpl->OnNavigationFinished(context);
+    self.webStateImpl->OnNavigationFinished(context);
     transition = context->GetPageTransition();
     bool transitionIsLink = ui::PageTransitionTypeIncludingQualifiersIs(
         transition, ui::PAGE_TRANSITION_LINK);
@@ -3367,32 +3803,32 @@
       transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
     }
   }
-  web::DownloadController::FromBrowserState(_webStateImpl->GetBrowserState())
-      ->CreateDownloadTask(_webStateImpl, [NSUUID UUID].UUIDString, responseURL,
-                           contentDisposition, contentLength, MIMEType,
-                           transition);
+  web::DownloadController::FromBrowserState(
+      self.webStateImpl->GetBrowserState())
+      ->CreateDownloadTask(self.webStateImpl, [NSUUID UUID].UUIDString,
+                           responseURL, contentDisposition, contentLength,
+                           MIMEType, transition);
 }
 
-#pragma mark -
-#pragma mark WebUI
+#pragma mark - WebUI
 
 - (void)createWebUIForURL:(const GURL&)URL {
   // |CreateWebUI| will do nothing if |URL| is not a WebUI URL and then
   // |HasWebUI| will return false.
-  _webStateImpl->CreateWebUI(URL);
-  bool isWebUIURL = _webStateImpl->HasWebUI();
+  self.webStateImpl->CreateWebUI(URL);
+  bool isWebUIURL = self.webStateImpl->HasWebUI();
   if (isWebUIURL && !web::features::WebUISchemeHandlingEnabled()) {
-    _webUIManager = [[CRWWebUIManager alloc] initWithWebState:_webStateImpl];
+    _webUIManager =
+        [[CRWWebUIManager alloc] initWithWebState:self.webStateImpl];
   }
 }
 
 - (void)clearWebUI {
-  _webStateImpl->ClearWebUI();
+  self.webStateImpl->ClearWebUI();
   _webUIManager = nil;
 }
 
-#pragma mark -
-#pragma mark Auth Challenge
+#pragma mark - Auth Challenge
 
 - (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
          forCertAcceptPolicy:(web::CertAcceptPolicy)policy
@@ -3442,7 +3878,7 @@
       [space.authenticationMethod isEqual:NSURLAuthenticationMethodNTLM] ||
       [space.authenticationMethod isEqual:NSURLAuthenticationMethodHTTPDigest]);
 
-  _webStateImpl->OnAuthRequired(
+  self.webStateImpl->OnAuthRequired(
       space, challenge.proposedCredential,
       base::BindRepeating(^(NSString* user, NSString* password) {
         [CRWWebController processHTTPAuthForUser:user
@@ -3469,133 +3905,10 @@
                  persistence:NSURLCredentialPersistenceForSession]);
 }
 
-#pragma mark -
-#pragma mark JavaScript Dialog
-
-- (void)runJavaScriptDialogOfType:(web::JavaScriptDialogType)type
-                 initiatedByFrame:(WKFrameInfo*)frame
-                          message:(NSString*)message
-                      defaultText:(NSString*)defaultText
-                       completion:(void (^)(BOOL, NSString*))completionHandler {
-  DCHECK(completionHandler);
-
-  // JavaScript dialogs should not be presented if there is no information about
-  // the requesting page's URL.
-  GURL requestURL = net::GURLWithNSURL(frame.request.URL);
-  if (!requestURL.is_valid()) {
-    completionHandler(NO, nil);
-    return;
-  }
-
-  self.webStateImpl->RunJavaScriptDialog(
-      requestURL, type, message, defaultText,
-      base::BindOnce(^(bool success, NSString* input) {
-        completionHandler(success, input);
-      }));
-}
-
-#pragma mark -
-#pragma mark TouchTracking
-
-- (void)touched:(BOOL)touched {
-  _clickInProgress = touched;
-  if (touched) {
-    self.userInteractionRegistered = YES;
-    _userInteractedWithWebController = YES;
-    if (_isBeingDestroyed)
-      return;
-    const NavigationManagerImpl* navigationManager = self.navigationManagerImpl;
-    GURL mainDocumentURL =
-        navigationManager->GetLastCommittedItem()
-            ? navigationManager->GetLastCommittedItem()->GetURL()
-            : [self currentURL];
-    _lastUserInteraction =
-        std::make_unique<UserInteractionEvent>(mainDocumentURL);
-  }
-}
-
-- (CRWTouchTrackingRecognizer*)touchTrackingRecognizer {
-  if (!_touchTrackingRecognizer) {
-    _touchTrackingRecognizer =
-        [[CRWTouchTrackingRecognizer alloc] initWithDelegate:self];
-  }
-  return _touchTrackingRecognizer;
-}
-
-- (BOOL)userIsInteracting {
-  // If page transfer started after last click, user is deemed to be no longer
-  // interacting.
-  if (!_lastUserInteraction ||
-      _lastTransferTimeInSeconds > _lastUserInteraction->time) {
-    return NO;
-  }
-  return [self userClickedRecently];
-}
-
-- (BOOL)userClickedRecently {
-  // Scrolling generates a pair of touch on/off event which causes
-  // _lastUserInteraction to register that there was user interaction.
-  // Checks for scrolling first to override time-based click heuristics.
-  BOOL scrolling = [[self webScrollView] isDragging] ||
-                   [[self webScrollView] isDecelerating];
-  if (scrolling)
-    return NO;
-  if (!_lastUserInteraction)
-    return NO;
-  return _clickInProgress ||
-         ((CFAbsoluteTimeGetCurrent() - _lastUserInteraction->time) <
-          kMaximumDelayForUserInteractionInSeconds);
-}
-
-#pragma mark -
-#pragma mark Session Information
-
-- (CRWSessionController*)sessionController {
-  NavigationManagerImpl* navigationManager = self.navigationManagerImpl;
-  return navigationManager ? navigationManager->GetSessionController() : nil;
-}
-
-- (NavigationManagerImpl*)navigationManagerImpl {
-  return _webStateImpl ? &(_webStateImpl->GetNavigationManagerImpl()) : nil;
-}
-
-- (BOOL)hasOpener {
-  return _webStateImpl ? _webStateImpl->HasOpener() : NO;
-}
-
-- (web::NavigationItemImpl*)currentNavItem {
-  return self.navigationManagerImpl
-             ? self.navigationManagerImpl->GetCurrentItemImpl()
-             : nullptr;
-}
-
-- (ui::PageTransition)currentTransition {
-  if (self.currentNavItem)
-    return self.currentNavItem->GetTransitionType();
-  else
-    return ui::PageTransitionFromInt(0);
-}
-
-- (web::Referrer)currentNavItemReferrer {
-  web::NavigationItem* currentItem = self.currentNavItem;
-  return currentItem ? currentItem->GetReferrer() : web::Referrer();
-}
-
-- (NSDictionary*)currentHTTPHeaders {
-  web::NavigationItem* currentItem = self.currentNavItem;
-  return currentItem ? currentItem->GetHttpRequestHeaders() : nil;
-}
-
-- (void)forgetNullWKNavigation:(WKNavigation*)navigation {
-  if (!navigation)
-    [_navigationStates removeNavigation:navigation];
-}
-
-#pragma mark -
-#pragma mark CRWWebViewScrollViewProxyObserver
+#pragma mark - CRWWebViewScrollViewProxyObserver
 
 - (void)webViewScrollViewDidZoom:
-        (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
   _pageHasZoomed = YES;
 
   __weak UIScrollView* weakScrollView = self.webScrollView;
@@ -3634,9 +3947,6 @@
   }
 }
 
-#pragma mark -
-#pragma mark CRWWebViewScrollViewProxyObserver
-
 // Under WKWebView, JavaScript can execute asynchronously. User can start
 // scrolling and calls to window.scrollTo executed during scrolling will be
 // treated as "during user interaction" and can cause app to go fullscreen.
@@ -3655,19 +3965,7 @@
         completionHandler:nil];
 }
 
-#pragma mark -
-#pragma mark Page State
-
-- (void)recordStateInHistory {
-  // Only record the state if:
-  // - the current NavigationItem's URL matches the current URL, and
-  // - the user has interacted with the page.
-  web::NavigationItem* item = self.currentNavItem;
-  if (item && item->GetURL() == [self currentURL] &&
-      self.userInteractionRegistered) {
-    item->SetPageDisplayState(self.pageDisplayState);
-  }
-}
+#pragma mark - Page State
 
 - (void)restoreStateFromHistory {
   web::NavigationItem* item = self.currentNavItem;
@@ -3675,48 +3973,6 @@
     self.pageDisplayState = item->GetPageDisplayState();
 }
 
-- (web::PageDisplayState)pageDisplayState {
-  web::PageDisplayState displayState;
-  // If a native controller is present, record its display state instead of that
-  // of the underlying placeholder webview.
-  if (self.nativeController) {
-    if ([self.nativeController respondsToSelector:@selector(contentOffset)]) {
-      displayState.scroll_state().set_content_offset(
-          [self.nativeController contentOffset]);
-    }
-    if ([self.nativeController respondsToSelector:@selector(contentInset)]) {
-      displayState.scroll_state().set_content_inset(
-          [self.nativeController contentInset]);
-    }
-  } else if (_webView) {
-    displayState.set_scroll_state(web::PageScrollState(
-        self.scrollPosition, self.webScrollView.contentInset));
-    UIScrollView* scrollView = self.webScrollView;
-    displayState.zoom_state().set_minimum_zoom_scale(
-        scrollView.minimumZoomScale);
-    displayState.zoom_state().set_maximum_zoom_scale(
-        scrollView.maximumZoomScale);
-    displayState.zoom_state().set_zoom_scale(scrollView.zoomScale);
-  }
-  return displayState;
-}
-
-- (void)setPageDisplayState:(web::PageDisplayState)displayState {
-  if (!displayState.IsValid())
-    return;
-  if (_webView) {
-    // Page state is restored after a page load completes.  If the user has
-    // scrolled or changed the zoom scale while the page is still loading, don't
-    // restore any state since it will confuse the user.
-    web::PageDisplayState currentPageDisplayState = self.pageDisplayState;
-    if (currentPageDisplayState.scroll_state() ==
-            _displayStateOnStartLoading.scroll_state() &&
-        !_pageHasZoomed) {
-      [self applyPageDisplayState:displayState];
-    }
-  }
-}
-
 - (void)extractViewportTagWithCompletion:(ViewportStateCompletion)completion {
   DCHECK(completion);
   web::NavigationItem* currentItem = self.currentNavItem;
@@ -3751,7 +4007,7 @@
   // - After zooming occurs in a UIWebView that's displaying a page with a hard-
   // coded viewport width, the zoom will not be updated upon rotation
   // ( crbug.com/485055 ).
-  if (!_webView)
+  if (!self.webView)
     return;
   web::NavigationItem* currentItem = self.currentNavItem;
   if (!currentItem)
@@ -3807,7 +4063,7 @@
 }
 
 - (void)prepareToApplyWebViewScrollZoomScale {
-  id webView = _webView;
+  id webView = self.webView;
   if (![webView respondsToSelector:@selector(viewForZoomingInScrollView:)]) {
     return;
   }
@@ -3822,7 +4078,7 @@
 }
 
 - (void)finishApplyingWebViewScrollZoomScale {
-  id webView = _webView;
+  id webView = self.webView;
   if ([webView respondsToSelector:@selector(scrollViewDidEndZooming:
                                                            withView:
                                                             atScale:)] &&
@@ -3873,8 +4129,7 @@
   }
 }
 
-#pragma mark -
-#pragma mark Fullscreen
+#pragma mark - Fullscreen
 
 - (void)optOutScrollsToTopForSubviews {
   NSMutableArray* stack =
@@ -3888,8 +4143,7 @@
   }
 }
 
-#pragma mark -
-#pragma mark WebDelegate Calls
+#pragma mark - WebDelegate Calls
 
 - (BOOL)isMainFrameNavigationAction:(WKNavigationAction*)action {
   if (action.targetFrame) {
@@ -3919,9 +4173,9 @@
     [_SSLStatusUpdater setDelegate:self];
   }
   NSString* host = base::SysUTF8ToNSString(_documentURL.host());
-  BOOL hasOnlySecureContent = [_webView hasOnlySecureContent];
+  BOOL hasOnlySecureContent = [self.webView hasOnlySecureContent];
   base::ScopedCFTypeRef<SecTrustRef> trust;
-  trust.reset([_webView serverTrust], base::scoped_policy::RETAIN);
+  trust.reset([self.webView serverTrust], base::scoped_policy::RETAIN);
 
   [_SSLStatusUpdater updateSSLStatusForNavigationItem:currentNavItem
                                          withCertHost:host
@@ -3994,7 +4248,7 @@
 
   // Ask web client if this cert error should be allowed.
   web::GetWebClient()->AllowCertificateError(
-      _webStateImpl, net::MapCertStatusToNetError(info.cert_status), info,
+      self.webStateImpl, net::MapCertStatusToNetError(info.cert_status), info,
       net::GURLWithNSURL(requestURL), recoverable,
       base::BindRepeating(^(bool proceed) {
         if (proceed) {
@@ -4002,7 +4256,7 @@
           [_certVerificationController allowCert:leafCert
                                          forHost:host
                                           status:info.cert_status];
-          _webStateImpl->GetSessionCertificatePolicyCacheImpl()
+          self.webStateImpl->GetSessionCertificatePolicyCacheImpl()
               .RegisterAllowedCertificate(
                   leafCert, base::SysNSStringToUTF8(host), info.cert_status);
           // New navigation is a different navigation from the original one.
@@ -4079,16 +4333,16 @@
 }
 
 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config {
-  if (!_webView) {
+  if (!self.webView) {
     [self setWebView:[self webViewWithConfiguration:config]];
     // The following is not called in -setWebView: as the latter used in unit
     // tests with fake web view, which cannot be added to view hierarchy.
     CHECK(_webUsageEnabled) << "Tried to create a web view while suspended!";
 
-    DCHECK(_webView);
+    DCHECK(self.webView);
 
-    [_webView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
-                                  UIViewAutoresizingFlexibleHeight];
+    [self.webView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
+                                      UIViewAutoresizingFlexibleHeight];
 
     // Create a dependency between the |webView| pan gesture and BVC side swipe
     // gestures. Note: This needs to be added before the longPress recognizers
@@ -4102,19 +4356,19 @@
 
     web::BrowserState* browserState = self.webStateImpl->GetBrowserState();
     _contextMenuController =
-        [[CRWContextMenuController alloc] initWithWebView:_webView
+        [[CRWContextMenuController alloc] initWithWebView:self.webView
                                              browserState:browserState
                                        injectionEvaluator:self
                                                  delegate:self];
 
     // Add all additional gesture recognizers to the web view.
     for (UIGestureRecognizer* recognizer in _gestureRecognizers) {
-      [_webView addGestureRecognizer:recognizer];
+      [self.webView addGestureRecognizer:recognizer];
     }
 
     // WKWebViews with invalid or empty frames have exhibited rendering bugs, so
     // resize the view to match the container view upon creation.
-    [_webView setFrame:[_containerView bounds]];
+    [self.webView setFrame:[_containerView bounds]];
   }
 
   // If web view is not currently displayed and if the visible NavigationItem
@@ -4197,20 +4451,20 @@
     return;
 
   CRWWebViewContentView* webViewContentView =
-      [[CRWWebViewContentView alloc] initWithWebView:_webView
+      [[CRWWebViewContentView alloc] initWithWebView:self.webView
                                           scrollView:self.webScrollView];
   [_containerView displayWebViewContentView:webViewContentView];
 }
 
 - (void)removeWebView {
-  if (!_webView)
+  if (!self.webView)
     return;
 
-  _webStateImpl->CancelDialogs();
+  self.webStateImpl->CancelDialogs();
   self.navigationManagerImpl->DetachFromWebView();
 
   [self abortLoad];
-  [_webView removeFromSuperview];
+  [self.webView removeFromSuperview];
   [_containerView resetContent];
   [self setWebView:nil];
 
@@ -4231,8 +4485,8 @@
     [self removeWebView];
   }
   _webProcessCrashed = YES;
-  _webStateImpl->CancelDialogs();
-  _webStateImpl->OnRenderProcessGone();
+  self.webStateImpl->CancelDialogs();
+  self.webStateImpl->OnRenderProcessGone();
 }
 
 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider {
@@ -4250,7 +4504,7 @@
 
   return [_POSTRequestLoader
         loadPOSTRequest:request
-              inWebView:_webView
+              inWebView:self.webView
           messageRouter:messageRouter
       completionHandler:^(NSError* loadError) {
         if (loadError)
@@ -4265,28 +4519,28 @@
 - (void)loadHTML:(NSString*)HTML forURL:(const GURL&)URL {
   DCHECK(HTML.length);
   // Remove the transient content view.
-  _webStateImpl->ClearTransientContent();
+  self.webStateImpl->ClearTransientContent();
 
   _loadPhase = web::LOAD_REQUESTED;
 
   // Web View should not be created for App Specific URLs.
   if (!web::GetWebClient()->IsAppSpecificURL(URL)) {
     [self ensureWebViewCreated];
-    DCHECK(_webView) << "_webView null while trying to load HTML";
+    DCHECK(self.webView) << "self.webView null while trying to load HTML";
   }
   WKNavigation* navigation =
-      [_webView loadHTMLString:HTML baseURL:net::NSURLWithGURL(URL)];
+      [self.webView loadHTMLString:HTML baseURL:net::NSURLWithGURL(URL)];
   [_navigationStates setState:web::WKNavigationState::REQUESTED
                 forNavigation:navigation];
   std::unique_ptr<web::NavigationContextImpl> context;
   const ui::PageTransition loadHTMLTransition =
       ui::PageTransition::PAGE_TRANSITION_TYPED;
-  if (_webStateImpl->HasWebUI()) {
+  if (self.webStateImpl->HasWebUI()) {
     // WebUI uses |loadHTML:forURL:| to feed the content to web view. This
     // should not be treated as a navigation, but WKNavigationDelegate callbacks
     // still expect a valid context.
     context = web::NavigationContextImpl::CreateNavigationContext(
-        _webStateImpl, URL, /*has_user_gesture=*/true, loadHTMLTransition,
+        self.webStateImpl, URL, /*has_user_gesture=*/true, loadHTMLTransition,
         /*is_renderer_initiated=*/false);
     context->SetNavigationItemUniqueID(self.currentNavItem->GetUniqueID());
     if (web::features::StorePendingItemInContext()) {
@@ -4309,33 +4563,7 @@
   [_navigationStates setContext:std::move(context) forNavigation:navigation];
 }
 
-- (void)loadHTML:(NSString*)HTML forAppSpecificURL:(const GURL&)URL {
-  CHECK(web::GetWebClient()->IsAppSpecificURL(URL));
-  [self loadHTML:HTML forURL:URL];
-}
-
-- (void)stopLoading {
-  _stoppedWKNavigation = [_navigationStates lastAddedNavigation];
-
-  base::RecordAction(base::UserMetricsAction("Stop"));
-  // Discard the pending and transient entried before notifying the tab model
-  // observers of the change via |-abortLoad|.
-  self.navigationManagerImpl->DiscardNonCommittedItems();
-  [self abortLoad];
-  web::NavigationItem* item = self.currentNavItem;
-  GURL navigationURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
-  // If discarding the non-committed entries results in an app-specific URL,
-  // reload it in its native view.
-  if (!self.nativeController &&
-      [self shouldLoadURLInNativeView:navigationURL]) {
-    // RendererInitiated flag is meaningless for showing previous native
-    // content page. RendererInitiated is used as less previledged.
-    [self loadCurrentURLInNativeViewWithRendererInitiatedNavigation:YES];
-  }
-}
-
-#pragma mark -
-#pragma mark WKUIDelegate Methods
+#pragma mark - WKUIDelegate Methods
 
 - (WKWebView*)webView:(WKWebView*)webView
     createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration
@@ -4368,8 +4596,8 @@
                           web::NavigationActionInitiationType::kUserInitiated;
   }
 
-  WebState* childWebState =
-      _webStateImpl->CreateNewWebState(requestURL, openerURL, initiatedByUser);
+  WebState* childWebState = self.webStateImpl->CreateNewWebState(
+      requestURL, openerURL, initiatedByUser);
   if (!childWebState)
     return nil;
 
@@ -4390,7 +4618,7 @@
 
 - (void)webViewDidClose:(WKWebView*)webView {
   if (self.hasOpener)
-    _webStateImpl->CloseWebState();
+    self.webStateImpl->CloseWebState();
 }
 
 - (void)webView:(WKWebView*)webView
@@ -4474,8 +4702,33 @@
       previewingViewController);
 }
 
-#pragma mark -
-#pragma mark WKNavigationDelegate Methods
+#pragma mark - WKUIDelegate helper methods
+
+// Helper to respond to |webView:runJavaScript...| delegate methods.
+// |completionHandler| must not be nil.
+- (void)runJavaScriptDialogOfType:(web::JavaScriptDialogType)type
+                 initiatedByFrame:(WKFrameInfo*)frame
+                          message:(NSString*)message
+                      defaultText:(NSString*)defaultText
+                       completion:(void (^)(BOOL, NSString*))completionHandler {
+  DCHECK(completionHandler);
+
+  // JavaScript dialogs should not be presented if there is no information about
+  // the requesting page's URL.
+  GURL requestURL = net::GURLWithNSURL(frame.request.URL);
+  if (!requestURL.is_valid()) {
+    completionHandler(NO, nil);
+    return;
+  }
+
+  self.webStateImpl->RunJavaScriptDialog(
+      requestURL, type, message, defaultText,
+      base::BindOnce(^(bool success, NSString* input) {
+        completionHandler(success, input);
+      }));
+}
+
+#pragma mark - WKNavigationDelegate Methods
 
 - (void)webView:(WKWebView*)webView
     decidePolicyForNavigationAction:(WKNavigationAction*)action
@@ -4576,7 +4829,7 @@
     // be detected. Display the confirmation dialog.
     if ([action.request.HTTPMethod isEqual:@"POST"] &&
         (action.navigationType == WKNavigationTypeFormResubmitted)) {
-      _webStateImpl->ShowRepostFormWarningDialog(
+      self.webStateImpl->ShowRepostFormWarningDialog(
           base::BindOnce(^(bool shouldContinue) {
             if (shouldContinue) {
               decisionHandler(WKNavigationActionPolicyAllow);
@@ -4584,7 +4837,7 @@
               decisionHandler(WKNavigationActionPolicyCancel);
               if (action.targetFrame.mainFrame) {
                 [_pendingNavigationInfo setCancelled:YES];
-                _webStateImpl->SetIsLoading(false);
+                self.webStateImpl->SetIsLoading(false);
               }
             }
           }));
@@ -4683,7 +4936,7 @@
       [self discardNonCommittedItemsIfLastCommittedWasNotNativeView];
 
       if (!_isBeingDestroyed && [self shouldClosePageOnNativeApplicationLoad])
-        _webStateImpl->CloseWebState();
+        self.webStateImpl->CloseWebState();
     }
 
     if (!_isBeingDestroyed) {
@@ -4691,7 +4944,7 @@
       // stopped because no other WKWebView callbacks are called.
       // TODO(crbug.com/767092): Loading should not start until webView.loading
       // is changed to YES.
-      _webStateImpl->SetIsLoading(false);
+      self.webStateImpl->SetIsLoading(false);
     }
   }
 
@@ -4769,13 +5022,8 @@
         static_cast<NSHTTPURLResponse*>(WKResponse.response));
     // TODO(crbug.com/551677): remove |OnHttpResponseHeadersReceived| and attach
     // headers to web::NavigationContext.
-    _webStateImpl->OnHttpResponseHeadersReceived(headers.get(), responseURL);
-  }
-
-  if (WKResponse.forMainFrame) {
-    web::NavigationContextImpl* context =
-        [self contextForPendingMainFrameNavigationWithURL:responseURL];
-    context->SetMimeType(WKResponse.response.MIMEType);
+    self.webStateImpl->OnHttpResponseHeadersReceived(headers.get(),
+                                                     responseURL);
   }
 
   // The page will not be changed until this navigation is committed, so the
@@ -4797,17 +5045,17 @@
       // Loading will be stopped in webView:didFinishNavigation: callback. This
       // call is here to preserve the original behavior when pending item is not
       // stored in NavigationContext.
-      _webStateImpl->SetIsLoading(false);
+      self.webStateImpl->SetIsLoading(false);
     }
   } else if (!shouldRenderResponse && WKResponse.forMainFrame) {
     [_pendingNavigationInfo setCancelled:YES];
   }
 
   if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
-      !WKResponse.forMainFrame && !_webView.loading) {
+      !WKResponse.forMainFrame && !self.webView.loading) {
     // This is the terminal callback for iframe navigation and there is no
     // pending main frame navigation. Last chance to flip IsLoading to false.
-    _webStateImpl->SetIsLoading(false);
+    self.webStateImpl->SetIsLoading(false);
   }
 
   handler(shouldRenderResponse ? WKNavigationResponsePolicyAllow
@@ -4854,7 +5102,7 @@
         context->SetUrl(webViewURL);
       }
     }
-    _webStateImpl->OnNavigationStarted(context);
+    self.webStateImpl->OnNavigationStarted(context);
     return;
   }
 
@@ -4897,7 +5145,7 @@
     return;
   }
 
-  _webStateImpl->GetNavigationManagerImpl()
+  self.webStateImpl->GetNavigationManagerImpl()
       .OnRendererInitiatedNavigationStarted(webViewURL);
 
   std::unique_ptr<web::NavigationContextImpl> navigationContext =
@@ -4911,7 +5159,7 @@
   // association between NavigationContextImpl and WKNavigation.
   [_navigationStates setContext:std::move(navigationContext)
                   forNavigation:navigation];
-  _webStateImpl->OnNavigationStarted(navigationContextPtr);
+  self.webStateImpl->OnNavigationStarted(navigationContextPtr);
   DCHECK(self.loadPhase == web::LOAD_REQUESTED);
 }
 
@@ -4972,7 +5220,7 @@
                  forNavigation:navigation
                provisionalLoad:YES];
   } else if (error.code == NSURLErrorUnsupportedURL &&
-             _webStateImpl->HasWebUI()) {
+             self.webStateImpl->HasWebUI()) {
     // This is a navigation to WebUI page.
     DCHECK(web::GetWebClient()->IsAppSpecificURL(
         net::GURLWithNSURL(error.userInfo[NSURLErrorFailingURLErrorKey])));
@@ -5020,7 +5268,7 @@
                   forNavigation:navigation];
   }
 
-  DCHECK_EQ(_webView, webView);
+  DCHECK_EQ(self.webView, webView);
   _certVerificationErrors->Clear();
 
   // Invariant: Every |navigation| should have a |context|. Note that violation
@@ -5055,13 +5303,16 @@
     webViewURL = currentWKItemURL;
   }
 
+  if (_pendingNavigationInfo.MIMEType)
+    context->SetMimeType(_pendingNavigationInfo.MIMEType);
+
   // Don't show webview for placeholder navigation to avoid covering the native
   // content, which may have already been shown.
   if (!IsPlaceholderUrl(webViewURL))
     [self displayWebView];
 
   // Update HTTP response headers.
-  _webStateImpl->UpdateHttpResponseHeaders(webViewURL);
+  self.webStateImpl->UpdateHttpResponseHeaders(webViewURL);
 
   if (@available(iOS 11.3, *)) {
     // On iOS 11.3 didReceiveServerRedirectForProvisionalNavigation: is not
@@ -5079,7 +5330,7 @@
   // finished.
   if (context) {
     web::NavigationManager* navigationManager =
-        _webStateImpl->GetNavigationManager();
+        self.webStateImpl->GetNavigationManager();
     if ((navigationManager->GetPendingItem() &&
          navigationManager->GetPendingItem()->GetURL() == webViewURL) ||
         (context->IsLoadingHtmlString()) ||
@@ -5097,7 +5348,7 @@
       //    crbug.com/676129)
       context->SetHasCommitted(true);
     }
-    context->SetResponseHeaders(_webStateImpl->GetHttpResponseHeaders());
+    context->SetResponseHeaders(self.webStateImpl->GetHttpResponseHeaders());
     self.webStateImpl->SetContentsMimeType(
         base::SysNSStringToUTF8(context->GetMimeType()));
   }
@@ -5192,90 +5443,19 @@
     [self updateSSLStatusForCurrentNavigationItem];
     [self updateHTML5HistoryState];
     if (!context->IsLoadingErrorPage() && !IsRestoreSessionUrl(webViewURL)) {
-      [self setNavigationItemTitle:[_webView title]];
+      [self setNavigationItemTitle:self.webView.title];
     }
   }
 
   // Report cases where SSL cert is missing for a secure connection.
   if (_documentURL.SchemeIsCryptographic()) {
     scoped_refptr<net::X509Certificate> cert;
-    cert = web::CreateCertFromTrust([_webView serverTrust]);
+    cert = web::CreateCertFromTrust(self.webView.serverTrust);
     UMA_HISTOGRAM_BOOLEAN("WebController.WKWebViewHasCertForSecureConnection",
                           static_cast<bool>(cert));
   }
 }
 
-- (void)didFinishGoToIndexSameDocumentNavigationWithType:
-            (web::NavigationInitiationType)type
-                                          hasUserGesture:(BOOL)hasUserGesture {
-  web::NavigationItem* item =
-      _webStateImpl->GetNavigationManager()->GetLastCommittedItem();
-  GURL URL = item->GetVirtualURL();
-  std::unique_ptr<web::NavigationContextImpl> context =
-      web::NavigationContextImpl::CreateNavigationContext(
-          _webStateImpl, URL, hasUserGesture,
-          static_cast<ui::PageTransition>(
-              item->GetTransitionType() |
-              ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK),
-          type == web::NavigationInitiationType::RENDERER_INITIATED);
-  context->SetIsSameDocument(true);
-  _webStateImpl->SetIsLoading(true);
-  _webStateImpl->OnNavigationStarted(context.get());
-  [self updateHTML5HistoryState];
-  [self setDocumentURL:URL context:context.get()];
-  context->SetHasCommitted(true);
-  _webStateImpl->OnNavigationFinished(context.get());
-  [self didFinishWithURL:URL loadSuccess:YES context:context.get()];
-}
-
-- (void)goToBackForwardListItem:(WKBackForwardListItem*)wk_item
-                 navigationItem:(web::NavigationItem*)item
-       navigationInitiationType:(web::NavigationInitiationType)type
-                 hasUserGesture:(BOOL)hasUserGesture {
-  WKNavigation* navigation = [_webView goToBackForwardListItem:wk_item];
-
-  GURL URL = net::GURLWithNSURL(wk_item.URL);
-  if (IsPlaceholderUrl(URL)) {
-    // No need to create navigation context for placeholder back forward
-    // navigations. Future callbacks do not expect that context will exist.
-    return;
-  }
-
-  // This navigation can be an iframe navigation, but it's not possible to
-  // distinguish it from the main frame navigation, so context still has to be
-  // created.
-  std::unique_ptr<web::NavigationContextImpl> context =
-      web::NavigationContextImpl::CreateNavigationContext(
-          _webStateImpl, URL, hasUserGesture,
-          static_cast<ui::PageTransition>(
-              item->GetTransitionType() |
-              ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK),
-          type == web::NavigationInitiationType::RENDERER_INITIATED);
-  context->SetNavigationItemUniqueID(item->GetUniqueID());
-  if (!navigation) {
-    // goToBackForwardListItem: returns nil for same-document back forward
-    // navigations.
-    context->SetIsSameDocument(true);
-  } else {
-    _webStateImpl->SetIsLoading(true);
-    _loadPhase = web::LOAD_REQUESTED;
-  }
-
-  web::WKBackForwardListItemHolder* holder =
-      web::WKBackForwardListItemHolder::FromNavigationItem(item);
-  holder->set_navigation_type(WKNavigationTypeBackForward);
-  context->SetIsPost((holder && [holder->http_method() isEqual:@"POST"]) ||
-                     item->HasPostData());
-
-  if (holder) {
-    context->SetMimeType(holder->mime_type());
-  }
-
-  [_navigationStates setContext:std::move(context) forNavigation:navigation];
-  [_navigationStates setState:web::WKNavigationState::REQUESTED
-                forNavigation:navigation];
-}
-
 - (void)webView:(WKWebView*)webView
     didFinishNavigation:(WKNavigation*)navigation {
   [self didReceiveWebViewNavigationDelegateCallback];
@@ -5476,8 +5656,20 @@
   [self webViewWebProcessDidCrash];
 }
 
-#pragma mark -
-#pragma mark CRWSSLStatusUpdater DataSource/Delegate Methods
+#pragma mark - WKNavigationDelegate helper method
+
+// WKNavigation objects are used as a weak key to store web::NavigationContext.
+// WKWebView manages WKNavigation lifetime and destroys them after the
+// navigation is finished. However for window opening navigations WKWebView
+// passes null WKNavigation to WKNavigationDelegate callbacks and strong key is
+// used to store web::NavigationContext. Those "null" navigations have to be
+// cleaned up manually by calling this method.
+- (void)forgetNullWKNavigation:(WKNavigation*)navigation {
+  if (!navigation)
+    [_navigationStates removeNavigation:navigation];
+}
+
+#pragma mark - CRWSSLStatusUpdaterDataSource
 
 - (void)SSLStatusUpdater:(CRWSSLStatusUpdater*)SSLStatusUpdater
     querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
@@ -5488,28 +5680,32 @@
                                     completionHandler:completionHandler];
 }
 
+#pragma mark - CRWSSLStatusUpdaterDelegate
+
 - (void)SSLStatusUpdater:(CRWSSLStatusUpdater*)SSLStatusUpdater
     didChangeSSLStatusForNavigationItem:(web::NavigationItem*)navigationItem {
   web::NavigationItem* visibleItem =
-      _webStateImpl->GetNavigationManager()->GetVisibleItem();
+      self.webStateImpl->GetNavigationManager()->GetVisibleItem();
   if (navigationItem == visibleItem)
-    _webStateImpl->DidChangeVisibleSecurityState();
+    self.webStateImpl->DidChangeVisibleSecurityState();
 }
 
-#pragma mark -
-#pragma mark CRWWebContextMenuControllerDelegate methods
+#pragma mark - CRWContextMenuDelegate methods
 
 - (void)webView:(WKWebView*)webView
     handleContextMenu:(const web::ContextMenuParams&)params {
-  DCHECK(webView == _webView);
+  DCHECK(webView == self.webView);
   if (_isBeingDestroyed) {
     return;
   }
   self.webStateImpl->HandleContextMenu(params);
 }
 
-#pragma mark -
-#pragma mark CRWNativeContentDelegate methods
+#pragma mark - CRWNativeContentDelegate methods
+
+- (void)nativeContent:(id)content titleDidChange:(NSString*)title {
+  [self setNavigationItemTitle:title];
+}
 
 - (void)nativeContent:(id)content
     handleContextMenu:(const web::ContextMenuParams&)params {
@@ -5519,20 +5715,7 @@
   self.webStateImpl->HandleContextMenu(params);
 }
 
-#pragma mark -
-#pragma mark CRWSessionControllerDelegate methods
-
-- (web::NavigationItemImpl*)pendingItemForSessionController:
-    (CRWSessionController*)sessionController {
-  WKNavigation* navigation =
-      [_navigationStates lastNavigationWithPendingItemInNavigationContext];
-  if (!navigation)
-    return nullptr;
-  return [_navigationStates contextForNavigation:navigation] -> GetItem();
-}
-
-#pragma mark -
-#pragma mark KVO Observation
+#pragma mark - KVO Observation
 
 - (void)observeValueForKeyPath:(NSString*)keyPath
                       ofObject:(id)object
@@ -5557,12 +5740,15 @@
   }
 }
 
+// Called when WKWebView estimatedProgress has been changed.
 - (void)webViewEstimatedProgressDidChange {
   if (!_isBeingDestroyed) {
-    self.webStateImpl->SendChangeLoadProgress([_webView estimatedProgress]);
+    self.webStateImpl->SendChangeLoadProgress(self.webView.estimatedProgress);
   }
 }
 
+// Called when WKWebView certificateChain or hasOnlySecureContent property has
+// changed.
 - (void)webViewSecurityFeaturesDidChange {
   if (self.loadPhase == web::LOAD_REQUESTED) {
     // Do not update SSL Status for pending load. It will be updated in
@@ -5572,11 +5758,12 @@
   [self updateSSLStatusForCurrentNavigationItem];
 }
 
+// Called when WKWebView loading state has been changed.
 - (void)webViewLoadingStateDidChange {
-  if (_webView.loading)
+  if (self.webView.loading)
     return;
 
-  GURL webViewURL = net::GURLWithNSURL([_webView URL]);
+  GURL webViewURL = net::GURLWithNSURL(self.webView.URL);
 
   if (![self isCurrentNavigationBackForward])
     return;
@@ -5601,7 +5788,7 @@
   if (web::GetWebClient()->IsSlimNavigationManagerEnabled() &&
       IsRestoreSessionUrl(webViewURL)) {
     if (previousURLHasAboutScheme || is_back_forward_navigation) {
-      [_webView reload];
+      [self.webView reload];
       _loadPhase = web::LOAD_REQUESTED;
       return;
     }
@@ -5635,7 +5822,7 @@
                     placeholderNavigation:IsPlaceholderUrl(webViewURL)];
       [self webPageChangedWithContext:newContext.get()];
       newContext->SetHasCommitted(!isSameDocumentNavigation);
-      _webStateImpl->OnNavigationFinished(newContext.get());
+      self.webStateImpl->OnNavigationFinished(newContext.get());
       // TODO(crbug.com/792515): It is OK, but very brittle, to call
       // |didFinishNavigation:| here because the gating condition is mutually
       // exclusive with the condition below. Refactor this method after
@@ -5646,14 +5833,15 @@
     } else {
       // Same document navigation does not contain response headers.
       net::HttpResponseHeaders* headers =
-          isSameDocumentNavigation ? nullptr
-                                   : _webStateImpl->GetHttpResponseHeaders();
+          isSameDocumentNavigation
+              ? nullptr
+              : self.webStateImpl->GetHttpResponseHeaders();
       existingContext->SetResponseHeaders(headers);
       existingContext->SetIsSameDocument(isSameDocumentNavigation);
       existingContext->SetHasCommitted(!isSameDocumentNavigation);
-      _webStateImpl->OnNavigationStarted(existingContext);
+      self.webStateImpl->OnNavigationStarted(existingContext);
       [self webPageChangedWithContext:existingContext];
-      _webStateImpl->OnNavigationFinished(existingContext);
+      self.webStateImpl->OnNavigationFinished(existingContext);
     }
   }
 
@@ -5661,11 +5849,12 @@
   [self didFinishNavigation:existingContext];
 }
 
+// Called when WKWebView title has been changed.
 - (void)webViewTitleDidChange {
   // WKWebView's title becomes empty when the web process dies; ignore that
   // update.
   if (_webProcessCrashed) {
-    DCHECK_EQ([_webView title].length, 0U);
+    DCHECK_EQ(self.webView.title.length, 0U);
     return;
   }
 
@@ -5677,28 +5866,30 @@
       lastNavigationState == web::WKNavigationState::REDIRECTED;
 
   if (!hasPendingNavigation &&
-      !IsPlaceholderUrl(net::GURLWithNSURL(_webView.URL))) {
+      !IsPlaceholderUrl(net::GURLWithNSURL(self.webView.URL))) {
     // Do not update the title if there is a navigation in progress because
     // there is no way to tell if KVO change fired for new or previous page.
-    [self setNavigationItemTitle:[_webView title]];
+    [self setNavigationItemTitle:self.webView.title];
   }
 }
 
+// Called when WKWebView canGoForward/canGoBack state has been changed.
 - (void)webViewBackForwardStateDidChange {
   // Don't trigger for LegacyNavigationManager because its back/foward state
   // doesn't always match that of WKWebView.
   if (web::GetWebClient()->IsSlimNavigationManagerEnabled())
-    _webStateImpl->OnBackForwardStateChanged();
+    self.webStateImpl->OnBackForwardStateChanged();
 }
 
+// Called when WKWebView URL has been changed.
 - (void)webViewURLDidChange {
   // TODO(stuartmorgan): Determine if there are any cases where this still
   // happens, and if so whether anything should be done when it does.
-  if (![_webView URL]) {
+  if (!self.webView.URL) {
     DVLOG(1) << "Received nil URL callback";
     return;
   }
-  GURL URL(net::GURLWithNSURL([_webView URL]));
+  GURL URL(net::GURLWithNSURL(self.webView.URL));
   // URL changes happen at four points:
   // 1) When a load starts; at this point, the load is provisional, and
   //    it should be ignored until it's committed, since the document/window
@@ -5729,13 +5920,13 @@
   // spoof the origin. On a document-changing URL change, the
   // window.location.href will match the previous URL at this stage, not the web
   // view's current URL.
-  if (![_webView isLoading]) {
+  if (!self.webView.loading) {
     if (_documentURL == URL) {
       if (![self isSafeBrowsingWarningDisplayedInWebView])
         return;
 
       self.navigationManagerImpl->DiscardNonCommittedItems();
-      _webStateImpl->SetIsLoading(false);
+      self.webStateImpl->SetIsLoading(false);
       _pendingNavigationInfo = nil;
       if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
         // Right after a history navigation that gets cancelled by a tap on
@@ -5746,35 +5937,37 @@
         // WKBasedNavigationManagerImpl::WKWebViewCache::GetCurrentItemIndex()
         // will be the index of the unsafe page's item. To get back into a
         // consistent state, force a reload.
-        [_webView reload];
+        [self.webView reload];
       } else {
         // Tapping "Go Back" on a SafeBrowsing interstitial can change whether
         // there are any forward or back items, e.g., by returning to or
         // moving away from the forward-most or back-most item.
-        _webStateImpl->OnBackForwardStateChanged();
+        self.webStateImpl->OnBackForwardStateChanged();
       }
       return;
     }
 
-    // At this point, _webView, _webView.backForwardList.currentItem and its
-    // associated NavigationItem should all have the same URL, except in two
+    // At this point, self.webView, self.webView.backForwardList.currentItem and
+    // its associated NavigationItem should all have the same URL, except in two
     // edge cases:
-    // 1. location.replace that only changes hash: WebKit updates _webView.URL
+    // 1. location.replace that only changes hash: WebKit updates
+    // self.webView.URL
     //    and currentItem.URL, and NavigationItem URL must be synced.
-    // 2. location.replace to about: URL: a WebKit bug causes only _webView.URL,
+    // 2. location.replace to about: URL: a WebKit bug causes only
+    // self.webView.URL,
     //    but not currentItem.URL to be updated. NavigationItem URL should be
-    //    synced to _webView.URL.
+    //    synced to self.webView.URL.
     // This needs to be done before |URLDidChangeWithoutDocumentChange| so any
     // WebStateObserver callbacks will see the updated URL.
-    // TODO(crbug.com/809287) use currentItem.URL instead of _webView.URL to
+    // TODO(crbug.com/809287) use currentItem.URL instead of self.webView.URL to
     // update NavigationItem URL.
     if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) {
-      const GURL webViewURL = net::GURLWithNSURL(_webView.URL);
+      const GURL webViewURL = net::GURLWithNSURL(self.webView.URL);
       web::NavigationItem* currentItem = nullptr;
-      if (_webView.backForwardList.currentItem) {
+      if (self.webView.backForwardList.currentItem) {
         currentItem = [[CRWNavigationItemHolder
-            holderForBackForwardListItem:_webView.backForwardList.currentItem]
-            navigationItem];
+            holderForBackForwardListItem:self.webView.backForwardList
+                                             .currentItem] navigationItem];
       } else {
         // WKBackForwardList.currentItem may be nil in a corner case when
         // location.replace is called with about:blank#hash in an empty window
@@ -5791,12 +5984,12 @@
     [self URLDidChangeWithoutDocumentChange:URL];
   } else if ([self isKVOChangePotentialSameDocumentNavigationToURL:URL]) {
     WKNavigation* navigation = [_navigationStates lastAddedNavigation];
-    [_webView
+    [self.webView
         evaluateJavaScript:@"window.location.href"
          completionHandler:^(id result, NSError* error) {
            // If the web view has gone away, or the location
            // couldn't be retrieved, abort.
-           if (!_webView || ![result isKindOfClass:[NSString class]]) {
+           if (!self.webView || ![result isKindOfClass:[NSString class]]) {
              return;
            }
            GURL JSURL(base::SysNSStringToUTF8(result));
@@ -5815,7 +6008,7 @@
            // change occurs immediately after. Revisit heuristics to
            // prevent this.
            BOOL webViewURLMatchesNewURL =
-               net::GURLWithNSURL([_webView URL]) == URL;
+               net::GURLWithNSURL(self.webView.URL) == URL;
            // Check that the new URL is different from the current
            // document URL. If not, URL change should not be reported.
            BOOL URLDidChangeFromDocumentURL = URL != _documentURL;
@@ -5841,6 +6034,12 @@
   }
 }
 
+#pragma mark - KVO helper methods
+
+// Returns YES if a KVO change to |newURL| could be a 'navigation' within the
+// document (hash change, pushState/replaceState, etc.). This should only be
+// used in the context of a URL KVO callback firing, and only if |isLoading| is
+// YES for the web view (since if it's not, no guesswork is needed).
 - (BOOL)isKVOChangePotentialSameDocumentNavigationToURL:(const GURL&)newURL {
   // If the origin changes, it can't be same-document.
   if (_documentURL.GetOrigin().is_empty() ||
@@ -5859,8 +6058,10 @@
   return YES;
 }
 
+// Called when a non-document-changing URL change occurs. Updates the
+// _documentURL, and informs the superclass of the change.
 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
-  DCHECK(newURL == net::GURLWithNSURL([_webView URL]));
+  DCHECK(newURL == net::GURLWithNSURL(self.webView.URL));
 
   if (base::FeatureList::IsEnabled(
           web::features::kCrashOnUnexpectedURLChange)) {
@@ -5922,7 +6123,7 @@
                                 ? newNavigationContext->GetItem()
                                 : self.navigationManagerImpl->GetPendingItem();
         if (pendingItem)
-          pendingItem->SetTitle(_webStateImpl->GetTitle());
+          pendingItem->SetTitle(self.webStateImpl->GetTitle());
       }
     }
   }
@@ -5938,272 +6139,19 @@
           [self contextForPendingMainFrameNavigationWithURL:newURL];
     }
     navigationContext->SetIsSameDocument(true);
-    _webStateImpl->OnNavigationStarted(navigationContext);
+    self.webStateImpl->OnNavigationStarted(navigationContext);
     [self didStartLoading];
     self.navigationManagerImpl->CommitPendingItem(
         navigationContext->ReleaseItem());
     navigationContext->SetHasCommitted(true);
-    _webStateImpl->OnNavigationFinished(navigationContext);
+    self.webStateImpl->OnNavigationFinished(navigationContext);
 
     [self updateSSLStatusForCurrentNavigationItem];
     [self didFinishNavigation:navigationContext];
   }
 }
 
-- (web::NavigationContextImpl*)contextForPendingMainFrameNavigationWithURL:
-    (const GURL&)URL {
-  // Here the enumeration variable |navigation| is __strong to allow setting it
-  // to nil.
-  for (__strong id navigation in [_navigationStates pendingNavigations]) {
-    if (navigation == [NSNull null]) {
-      // null is a valid navigation object passed to WKNavigationDelegate
-      // callbacks and represents window opening action.
-      navigation = nil;
-    }
-
-    web::NavigationContextImpl* context =
-        [_navigationStates contextForNavigation:navigation];
-    if (context && context->GetUrl() == URL) {
-      return context;
-    }
-  }
-  return nullptr;
-}
-
-- (void)loadRequestForCurrentNavigationItem {
-  DCHECK(_webView);
-  DCHECK(self.currentNavItem);
-  // If a load is kicked off on a WKWebView with a frame whose size is {0, 0} or
-  // that has a negative dimension for a size, rendering issues occur that
-  // manifest in erroneous scrolling and tap handling (crbug.com/574996,
-  // crbug.com/577793).
-  DCHECK_GT(CGRectGetWidth([_webView frame]), 0.0);
-  DCHECK_GT(CGRectGetHeight([_webView frame]), 0.0);
-
-  // If the current item uses a different user agent from that is currently used
-  // in the web view, update |customUserAgent| property, which will be used by
-  // the next request sent by this web view.
-  web::UserAgentType itemUserAgentType =
-      self.currentNavItem->GetUserAgentType();
-  if (itemUserAgentType != web::UserAgentType::NONE) {
-    NSString* userAgentString = base::SysUTF8ToNSString(
-        web::GetWebClient()->GetUserAgent(itemUserAgentType));
-    if (![_webView.customUserAgent isEqualToString:userAgentString]) {
-      _webView.customUserAgent = userAgentString;
-    }
-  }
-
-  web::WKBackForwardListItemHolder* holder =
-      [self currentBackForwardListItemHolder];
-  BOOL repostedForm =
-      [holder->http_method() isEqual:@"POST"] &&
-      (holder->navigation_type() == WKNavigationTypeFormResubmitted ||
-       holder->navigation_type() == WKNavigationTypeFormSubmitted);
-  web::NavigationItemImpl* currentItem = self.currentNavItem;
-  NSData* POSTData = currentItem->GetPostData();
-  NSMutableURLRequest* request = [self requestForCurrentNavigationItem];
-
-  BOOL sameDocumentNavigation = currentItem->IsCreatedFromPushState() ||
-                                currentItem->IsCreatedFromHashChange();
-
-  if (holder->back_forward_list_item()) {
-    // Check if holder's WKBackForwardListItem still correctly represents
-    // navigation item. With LegacyNavigationManager, replaceState operation
-    // creates a new navigation item, leaving the old item committed. That
-    // old committed item will be associated with WKBackForwardListItem whose
-    // state was replaced. So old item won't have correct WKBackForwardListItem.
-    if (net::GURLWithNSURL(holder->back_forward_list_item().URL) !=
-        currentItem->GetURL()) {
-      // The state was replaced for this item. The item should not be a part of
-      // committed items, but it's too late to remove the item. Cleaup
-      // WKBackForwardListItem and mark item with "state replaced" flag.
-      currentItem->SetHasStateBeenReplaced(true);
-      holder->set_back_forward_list_item(nil);
-    }
-  }
-
-  // If the request has POST data and is not a repost form, configure and
-  // run the POST request.
-  if (POSTData.length && !repostedForm) {
-    [request setHTTPMethod:@"POST"];
-    [request setHTTPBody:POSTData];
-    [request setAllHTTPHeaderFields:self.currentHTTPHeaders];
-    // As of iOS 11, WKWebView supports requests with POST data, so the
-    // Javascript POST workaround only needs to be used if the OS version is
-    // less than iOS 11.
-    // TODO(crbug.com/740987): Remove POST workaround once iOS 10 is dropped.
-    if (!base::ios::IsRunningOnIOS11OrLater()) {
-      GURL navigationURL =
-          currentItem ? currentItem->GetURL() : GURL::EmptyGURL();
-      std::unique_ptr<web::NavigationContextImpl> navigationContext =
-          [self registerLoadRequestForURL:navigationURL
-                                 referrer:self.currentNavItemReferrer
-                               transition:self.currentTransition
-                   sameDocumentNavigation:sameDocumentNavigation
-                           hasUserGesture:YES
-                        rendererInitiated:NO
-                    placeholderNavigation:NO];
-      WKNavigation* navigation = [self loadPOSTRequest:request];
-      [_navigationStates setContext:std::move(navigationContext)
-                      forNavigation:navigation];
-      [_navigationStates setState:web::WKNavigationState::REQUESTED
-                    forNavigation:navigation];
-      return;
-    }
-  }
-
-  ProceduralBlock defaultNavigationBlock = ^{
-    web::NavigationItem* item = self.currentNavItem;
-    GURL navigationURL = item ? item->GetURL() : GURL::EmptyGURL();
-    GURL contextURL = IsPlaceholderUrl(navigationURL)
-                          ? ExtractUrlFromPlaceholderUrl(navigationURL)
-                          : navigationURL;
-    std::unique_ptr<web::NavigationContextImpl> navigationContext =
-        [self registerLoadRequestForURL:contextURL
-                               referrer:self.currentNavItemReferrer
-                             transition:self.currentTransition
-                 sameDocumentNavigation:sameDocumentNavigation
-                         hasUserGesture:YES
-                      rendererInitiated:NO
-                  placeholderNavigation:IsPlaceholderUrl(navigationURL)];
-
-    WKNavigation* navigation = nil;
-    GURL virtualURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
-    if (navigationURL.SchemeIsFile() &&
-        web::GetWebClient()->IsAppSpecificURL(virtualURL)) {
-      // file:// URL navigations are allowed for app-specific URLs, which
-      // already have elevated privileges.
-      NSURL* navigationNSURL = net::NSURLWithGURL(navigationURL);
-      navigation = [_webView loadFileURL:navigationNSURL
-                 allowingReadAccessToURL:navigationNSURL];
-    } else {
-      navigation = [_webView loadRequest:request];
-    }
-    [_navigationStates setState:web::WKNavigationState::REQUESTED
-                  forNavigation:navigation];
-    [_navigationStates setContext:std::move(navigationContext)
-                    forNavigation:navigation];
-    [self reportBackForwardNavigationTypeForFastNavigation:NO];
-  };
-
-  // When navigating via WKBackForwardListItem to pages created or updated by
-  // calls to pushState() and replaceState(), sometimes web_bundle.js is not
-  // injected correctly.  This means that calling window.history navigation
-  // functions will invoke WKWebView's non-overridden implementations, causing a
-  // mismatch between the WKBackForwardList and NavigationManager.
-  // TODO(crbug.com/659816): Figure out how to prevent web_bundle.js injection
-  // flake.
-  if (currentItem->HasStateBeenReplaced() ||
-      currentItem->IsCreatedFromPushState()) {
-    defaultNavigationBlock();
-    return;
-  }
-
-  // If there is no corresponding WKBackForwardListItem, or the item is not in
-  // the current WKWebView's back-forward list, navigating using WKWebView API
-  // is not possible. In this case, fall back to the default navigation
-  // mechanism.
-  if (!holder->back_forward_list_item() ||
-      ![self isBackForwardListItemValid:holder->back_forward_list_item()]) {
-    defaultNavigationBlock();
-    return;
-  }
-
-  ProceduralBlock webViewNavigationBlock = ^{
-    // If the current navigation URL is the same as the URL of the visible
-    // page, that means the user requested a reload. |goToBackForwardListItem|
-    // will be a no-op when it is passed the current back forward list item,
-    // so |reload| must be explicitly called.
-    web::NavigationItem* item = self.currentNavItem;
-    GURL navigationURL = item ? item->GetURL() : GURL::EmptyGURL();
-    std::unique_ptr<web::NavigationContextImpl> navigationContext =
-        [self registerLoadRequestForURL:navigationURL
-                               referrer:self.currentNavItemReferrer
-                             transition:self.currentTransition
-                 sameDocumentNavigation:sameDocumentNavigation
-                         hasUserGesture:YES
-                      rendererInitiated:NO
-                  placeholderNavigation:NO];
-    WKNavigation* navigation = nil;
-    if (navigationURL == net::GURLWithNSURL([_webView URL])) {
-      navigation = [_webView reload];
-    } else {
-      // |didCommitNavigation:| may not be called for fast navigation, so update
-      // the navigation type now as it is already known.
-      navigationContext->SetWKNavigationType(WKNavigationTypeBackForward);
-      navigationContext->SetMimeType(holder->mime_type());
-      holder->set_navigation_type(WKNavigationTypeBackForward);
-      navigation =
-          [_webView goToBackForwardListItem:holder->back_forward_list_item()];
-      [self reportBackForwardNavigationTypeForFastNavigation:YES];
-    }
-    [_navigationStates setState:web::WKNavigationState::REQUESTED
-                  forNavigation:navigation];
-    [_navigationStates setContext:std::move(navigationContext)
-                    forNavigation:navigation];
-  };
-
-  // If the request is not a form submission or resubmission, or the user
-  // doesn't need to confirm the load, then continue right away.
-
-  if (!repostedForm || currentItem->ShouldSkipRepostFormConfirmation()) {
-    webViewNavigationBlock();
-    return;
-  }
-
-  // If the request is form submission or resubmission, then prompt the
-  // user before proceeding.
-  DCHECK(repostedForm);
-  DCHECK(!web::GetWebClient()->IsSlimNavigationManagerEnabled());
-  _webStateImpl->ShowRepostFormWarningDialog(
-      base::BindOnce(^(bool shouldContinue) {
-        if (_isBeingDestroyed)
-          return;
-
-        if (shouldContinue)
-          webViewNavigationBlock();
-        else
-          [self stopLoading];
-      }));
-}
-
-- (void)reportBackForwardNavigationTypeForFastNavigation:(BOOL)isFast {
-  // TODO(crbug.com/665189): Use NavigationManager::GetPendingItemIndex() once
-  // it returns correct result.
-  int pendingIndex = self.sessionController.pendingItemIndex;
-  if (pendingIndex == -1) {
-    // Pending navigation is not a back forward navigation.
-    return;
-  }
-
-  BOOL isBack =
-      pendingIndex < self.navigationManagerImpl->GetLastCommittedItemIndex();
-  BackForwardNavigationType type = BackForwardNavigationType::FAST_BACK;
-  if (isBack) {
-    type = isFast ? BackForwardNavigationType::FAST_BACK
-                  : BackForwardNavigationType::SLOW_BACK;
-  } else {
-    type = isFast ? BackForwardNavigationType::FAST_FORWARD
-                  : BackForwardNavigationType::SLOW_FORWARD;
-  }
-
-  UMA_HISTOGRAM_ENUMERATION(
-      "Navigation.IOSWKWebViewSlowFastBackForward", type,
-      BackForwardNavigationType::BACK_FORWARD_NAVIGATION_TYPE_COUNT);
-}
-
-- (void)setAllowsBackForwardNavigationGestures:
-    (BOOL)allowsBackForwardNavigationGestures {
-  // Store it to an instance variable as well as
-  // _webView.allowsBackForwardNavigationGestures because _webView may be nil.
-  // When _webView is nil, it will be set later in -setWebView:.
-  _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
-  _webView.allowsBackForwardNavigationGestures =
-      allowsBackForwardNavigationGestures;
-}
-
-#pragma mark -
-#pragma mark Testing-Only Methods
+#pragma mark - Testing-Only Methods
 
 - (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView {
   _currentURLLoadWasTrigerred = NO;
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 349d5b40..434fc67 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -1895,7 +1895,12 @@
 
 // Tests server redirect navigation.
 TEST_P(WebStateObserverTest, RedirectNavigation) {
-  const GURL url = test_server_->GetURL("/server-redirect?echoall");
+  const GURL url = test_server_->GetURL("/server-redirect-301?"
+                                        "server-redirect-302?"
+                                        "server-redirect-303?"
+                                        "server-redirect-307?"
+                                        "server-redirect-308?"
+                                        "echoall");
   const GURL redirect_url = test_server_->GetURL("/echoall");
 
   // Load url which replies with redirect.
@@ -1912,14 +1917,17 @@
       .WillOnce(VerifyPageStartedContext(
           web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context,
           &nav_id));
-  // Second ShouldAllowRequest call is for redirect_url.
+
+  // 5 calls on ShouldAllowRequest for redirections.
   WebStatePolicyDecider::RequestInfo expected_redirect_request_info(
       ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(
       *decider_,
       ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info)))
-      .WillOnce(Return(true));
+      .Times(5)
+      .WillRepeatedly(Return(true));
+
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
       .WillOnce(Return(true));
   EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _))
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 a5a901a1..06b549af 100644
--- a/ios/web_view/internal/web_view_web_main_parts.mm
+++ b/ios/web_view/internal/web_view_web_main_parts.mm
@@ -53,7 +53,6 @@
   std::string enable_features = base::JoinString(
       {autofill::features::kAutofillEnableAccountWalletStorage.name,
        autofill::features::kAutofillAlwaysShowServerCardsInSyncTransport.name,
-       switches::kSyncStandaloneTransport.name,
        switches::kSyncSupportSecondaryAccount.name,
        switches::kSyncUSSAutofillWalletData.name},
       ",");
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 96c801b..6838a451 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -6402,9 +6402,7 @@
   sources = [
     "reporting/reporting_policy.proto",
   ]
-  deps = [
-    "//testing/libfuzzer/proto:json_proto",
-  ]
+  link_deps = [ "//testing/libfuzzer/proto:json_proto" ]
 }
 
 fuzzer_test("net_reporting_header_parser_fuzzer") {
diff --git a/net/cert/internal/cert_error_params.cc b/net/cert/internal/cert_error_params.cc
index 851f8e3..f454572 100644
--- a/net/cert/internal/cert_error_params.cc
+++ b/net/cert/internal/cert_error_params.cc
@@ -4,6 +4,8 @@
 
 #include "net/cert/internal/cert_error_params.h"
 
+#include <memory>
+
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/der/input.h"
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index 4c0f933a..fcc73fc 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -4,6 +4,7 @@
 
 #include "net/cert/internal/path_builder.h"
 
+#include <memory>
 #include <set>
 #include <unordered_set>
 
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 701d8d5..b02fa0876 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -127,7 +127,7 @@
   for (std::string text_string : text_strings) {
     DCHECK(!text_string.empty());
 
-    rdata += base::checked_cast<char>(text_string.size());
+    rdata += base::checked_cast<unsigned char>(text_string.size());
     rdata += std::move(text_string);
   }
   record.SetOwnedRdata(std::move(rdata));
@@ -186,6 +186,7 @@
   MockTransaction(const MockDnsClientRuleList& rules,
                   const std::string& hostname,
                   uint16_t qtype,
+                  SecureDnsMode secure_dns_mode,
                   DnsTransactionFactory::CallbackType callback)
       : result_(MockDnsClientRule::FAIL),
         hostname_(hostname),
@@ -194,10 +195,13 @@
         secure_(false),
         started_(false),
         delayed_(false) {
-    // Find the relevant rule which matches |qtype| and prefix of |hostname|.
+    // Find the relevant rule which matches |qtype|, |secure_dns_mode|, and
+    // prefix of |hostname|.
     for (size_t i = 0; i < rules.size(); ++i) {
       const std::string& prefix = rules[i].prefix;
-      if ((rules[i].qtype == qtype) && (hostname.size() >= prefix.size()) &&
+      if ((rules[i].qtype == qtype) &&
+          rules[i].secure_dns_mode == secure_dns_mode &&
+          (hostname.size() >= prefix.size()) &&
           (hostname.compare(0, prefix.size(), prefix) == 0)) {
         const MockDnsClientRule::Result* result = &rules[i].result;
         result_ = MockDnsClientRule::Result(result->type);
@@ -432,6 +436,14 @@
 MockDnsClientRule::Result& MockDnsClientRule::Result::operator=(
     Result&& result) = default;
 
+// static
+MockDnsClientRule::Result MockDnsClientRule::CreateSecureResult(
+    std::unique_ptr<DnsResponse> response) {
+  auto result = Result(std::move(response));
+  result.secure = true;
+  return result;
+}
+
 // A DnsTransactionFactory which creates MockTransaction.
 class MockDnsClient::MockTransactionFactory : public DnsTransactionFactory {
  public:
@@ -445,18 +457,16 @@
       uint16_t qtype,
       DnsTransactionFactory::CallbackType callback,
       const NetLogWithSource&,
-      SecureDnsMode) override {
+      SecureDnsMode secure_dns_mode) override {
     std::unique_ptr<MockTransaction> transaction =
         std::make_unique<MockTransaction>(rules_, hostname, qtype,
-                                          std::move(callback));
+                                          secure_dns_mode, std::move(callback));
     if (transaction->delayed())
       delayed_transactions_.push_back(transaction->AsWeakPtr());
     return transaction;
   }
 
-  void AddEDNSOption(const OptRecordRdata::Opt& opt) override {
-    NOTREACHED() << "Not implemented";
-  }
+  void AddEDNSOption(const OptRecordRdata::Opt& opt) override {}
 
   void CompleteDelayedTransactions() {
     DelayedTransactionList old_delayed_transactions;
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index a72cbfae..f8a990b 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -17,6 +17,7 @@
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_response.h"
+#include "net/dns/dns_util.h"
 #include "net/dns/public/dns_protocol.h"
 
 namespace net {
@@ -215,20 +216,25 @@
     bool secure = false;
   };
 
+  static Result CreateSecureResult(std::unique_ptr<DnsResponse> response);
+
   // If |delay| is true, matching transactions will be delayed until triggered
   // by the consumer.
   MockDnsClientRule(const std::string& prefix_arg,
                     uint16_t qtype_arg,
+                    SecureDnsMode secure_dns_mode,
                     Result result_arg,
                     bool delay)
       : result(std::move(result_arg)),
         prefix(prefix_arg),
         qtype(qtype_arg),
+        secure_dns_mode(secure_dns_mode),
         delay(delay) {}
 
   Result result;
   std::string prefix;
   uint16_t qtype;
+  SecureDnsMode secure_dns_mode;
   bool delay;
 };
 
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index 040bcb4..72a1f8a 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -261,14 +261,15 @@
 
   // Checks whether this HostResolver has cached a resolution for the given
   // hostname (or IP address literal). If so, returns true and writes the source
-  // of the resolution (e.g. DNS, HOSTS file, etc.) to |source_out| and the
-  // staleness of the resolution to |stale_out| (if they are not null).
-  // It tries using two common address_family and host_resolver_flag
-  // combinations when checking the cache; this means false negatives are
-  // possible, but unlikely.
+  // of the resolution (e.g. DNS, HOSTS file, etc.) to |source_out|, the
+  // staleness of the resolution to |stale_out|, and whether the result was
+  // retrieved securely or not to |secure_out| (if they are not null). It tries
+  // using two common address_family and host_resolver_flag combinations when
+  // checking the cache; this means false negatives are possible, but unlikely.
   virtual bool HasCached(base::StringPiece hostname,
                          HostCache::Entry::Source* source_out,
-                         HostCache::EntryStaleness* stale_out) const = 0;
+                         HostCache::EntryStaleness* stale_out,
+                         bool* secure_out) const = 0;
 
   // Returns the current DNS configuration |this| is using, as a Value, or
   // nullptr if it's configured to always use the system host resolver.
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 6e38743..d0c75fa 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -2273,11 +2273,16 @@
 
 bool HostResolverImpl::HasCached(base::StringPiece hostname,
                                  HostCache::Entry::Source* source_out,
-                                 HostCache::EntryStaleness* stale_out) const {
+                                 HostCache::EntryStaleness* stale_out,
+                                 bool* secure_out) const {
   if (!cache_)
     return false;
 
-  return !!cache_->GetMatchingKey(hostname, source_out, stale_out);
+  const HostCache::Key* key =
+      cache_->GetMatchingKey(hostname, source_out, stale_out);
+  if (key && secure_out != nullptr)
+    *secure_out = key->secure;
+  return !!key;
 }
 
 std::unique_ptr<base::Value> HostResolverImpl::GetDnsConfigAsValue() const {
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h
index 149f9b9..893aeceb 100644
--- a/net/dns/host_resolver_impl.h
+++ b/net/dns/host_resolver_impl.h
@@ -153,7 +153,8 @@
   HostCache* GetHostCache() override;
   bool HasCached(base::StringPiece hostname,
                  HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out) const override;
+                 HostCache::EntryStaleness* stale_out,
+                 bool* secure_out) const override;
 
   std::unique_ptr<base::Value> GetDnsConfigAsValue() const override;
 
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc
index 2bfba68..1238156 100644
--- a/net/dns/host_resolver_impl_unittest.cc
+++ b/net/dns/host_resolver_impl_unittest.cc
@@ -2918,8 +2918,8 @@
                          uint16_t qtype,
                          MockDnsClientRule::ResultType result_type,
                          bool delay) {
-    rules->emplace_back(prefix, qtype, MockDnsClientRule::Result(result_type),
-                        delay);
+    rules->emplace_back(prefix, qtype, SecureDnsMode::AUTOMATIC,
+                        MockDnsClientRule::Result(result_type), delay);
   }
 
   static void AddDnsRule(MockDnsClientRuleList* rules,
@@ -2927,7 +2927,7 @@
                          uint16_t qtype,
                          const IPAddress& result_ip,
                          bool delay) {
-    rules->emplace_back(prefix, qtype,
+    rules->emplace_back(prefix, qtype, SecureDnsMode::AUTOMATIC,
                         MockDnsClientRule::Result(
                             BuildTestDnsResponse(prefix, std::move(result_ip))),
                         delay);
@@ -2940,7 +2940,7 @@
                          std::string cannonname,
                          bool delay) {
     rules->emplace_back(
-        prefix, qtype,
+        prefix, qtype, SecureDnsMode::AUTOMATIC,
         MockDnsClientRule::Result(BuildTestDnsResponse(
             prefix, std::move(result_ip), std::move(cannonname))),
         delay);
@@ -2953,7 +2953,8 @@
                                bool delay) {
     MockDnsClientRule::Result result(result_type);
     result.secure = true;
-    rules->emplace_back(prefix, qtype, std::move(result), delay);
+    rules->emplace_back(prefix, qtype, SecureDnsMode::AUTOMATIC,
+                        std::move(result), delay);
   }
 
   void ChangeDnsConfig(const DnsConfig& config) {
@@ -3470,9 +3471,9 @@
 TEST_F(HostResolverImplDnsTest, BypassDnsToMdnsWithNonAddress) {
   // Ensure DNS task and system (proc) requests will fail.
   MockDnsClientRuleList rules;
-  rules.emplace_back("myhello.local", dns_protocol::kTypeTXT,
-                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
-                     false /* delay */);
+  rules.emplace_back(
+      "myhello.local", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
+      MockDnsClientRule::Result(MockDnsClientRule::FAIL), false /* delay */);
   CreateResolver();
   UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
   proc_->AddRuleForAllFamilies(std::string(), std::string());
@@ -5004,7 +5005,7 @@
                                                         bar_records};
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(
                          BuildTestDnsResponse("host", std::move(text_records))),
                      false /* delay */);
@@ -5041,7 +5042,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::NODOMAIN),
                      false /* delay */);
 
@@ -5066,7 +5067,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::FAIL),
                      false /* delay */);
 
@@ -5091,7 +5092,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::TIMEOUT),
                      false /* delay */);
 
@@ -5116,7 +5117,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::EMPTY),
                      false /* delay */);
 
@@ -5141,7 +5142,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::MALFORMED),
                      false /* delay */);
 
@@ -5162,7 +5163,7 @@
 TEST_F(HostResolverImplDnsTest, TxtQuery_MismatchedName) {
   std::vector<std::vector<std::string>> text_records = {{"text"}};
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsResponse(
                          "host", std::move(text_records), "not.host")),
                      false /* delay */);
@@ -5184,7 +5185,7 @@
 TEST_F(HostResolverImplDnsTest, TxtQuery_WrongType) {
   // Respond to a TXT query with an A response.
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(
                          BuildTestDnsResponse("host", IPAddress(1, 2, 3, 4))),
                      false /* delay */);
@@ -5216,7 +5217,7 @@
                                                         bar_records};
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeTXT,
+  rules.emplace_back("host", dns_protocol::kTypeTXT, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(
                          BuildTestDnsResponse("host", std::move(text_records))),
                      false /* delay */);
@@ -5249,7 +5250,7 @@
 
 TEST_F(HostResolverImplDnsTest, PtrQuery) {
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsPointerResponse(
                          "host", {"foo.com", "bar.com"})),
                      false /* delay */);
@@ -5275,6 +5276,7 @@
 TEST_F(HostResolverImplDnsTest, PtrQuery_Ip) {
   MockDnsClientRuleList rules;
   rules.emplace_back("8.8.8.8", dns_protocol::kTypePTR,
+                     SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsPointerResponse(
                          "8.8.8.8", {"foo.com", "bar.com"})),
                      false /* delay */);
@@ -5304,7 +5306,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::NODOMAIN),
                      false /* delay */);
 
@@ -5329,7 +5331,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::FAIL),
                      false /* delay */);
 
@@ -5354,7 +5356,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::TIMEOUT),
                      false /* delay */);
 
@@ -5379,7 +5381,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::EMPTY),
                      false /* delay */);
 
@@ -5404,7 +5406,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::MALFORMED),
                      false /* delay */);
 
@@ -5425,7 +5427,7 @@
 TEST_F(HostResolverImplDnsTest, PtrQuery_MismatchedName) {
   std::vector<std::string> ptr_records = {{"foo.com"}};
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsPointerResponse(
                          "host", std::move(ptr_records), "not.host")),
                      false /* delay */);
@@ -5447,7 +5449,7 @@
 TEST_F(HostResolverImplDnsTest, PtrQuery_WrongType) {
   // Respond to a TXT query with an A response.
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(
                          BuildTestDnsResponse("host", IPAddress(1, 2, 3, 4))),
                      false /* delay */);
@@ -5473,7 +5475,7 @@
 // involved.
 TEST_F(HostResolverImplDnsTest, PtrDnsQuery) {
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypePTR,
+  rules.emplace_back("host", dns_protocol::kTypePTR, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsPointerResponse(
                          "host", {"foo.com", "bar.com"})),
                      false /* delay */);
@@ -5503,7 +5505,7 @@
   const TestServiceRecord kRecord3 = {5, 1, 5, "google.com"};
   const TestServiceRecord kRecord4 = {2, 100, 12345, "chromium.org"};
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsResponse(
                          "host", {kRecord1, kRecord2, kRecord3, kRecord4})),
                      false /* delay */);
@@ -5546,7 +5548,7 @@
   const TestServiceRecord kRecord1 = {5, 0, 80, "bar.com"};
   const TestServiceRecord kRecord2 = {5, 0, 5, "google.com"};
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(
                          BuildTestDnsResponse("host", {kRecord1, kRecord2})),
                      false /* delay */);
@@ -5576,7 +5578,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::NODOMAIN),
                      false /* delay */);
 
@@ -5601,7 +5603,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::FAIL),
                      false /* delay */);
 
@@ -5626,7 +5628,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::TIMEOUT),
                      false /* delay */);
 
@@ -5651,7 +5653,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::EMPTY),
                      false /* delay */);
 
@@ -5676,7 +5678,7 @@
   proc_->SignalMultiple(1u);
 
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(MockDnsClientRule::MALFORMED),
                      false /* delay */);
 
@@ -5697,7 +5699,7 @@
 TEST_F(HostResolverImplDnsTest, SrvQuery_MismatchedName) {
   std::vector<TestServiceRecord> srv_records = {{1, 2, 3, "foo.com"}};
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsResponse(
                          "host", std::move(srv_records), "not.host")),
                      false /* delay */);
@@ -5719,7 +5721,7 @@
 TEST_F(HostResolverImplDnsTest, SrvQuery_WrongType) {
   // Respond to a SRV query with an A response.
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(
                          BuildTestDnsResponse("host", IPAddress(1, 2, 3, 4))),
                      false /* delay */);
@@ -5749,7 +5751,7 @@
   const TestServiceRecord kRecord3 = {5, 1, 5, "google.com"};
   const TestServiceRecord kRecord4 = {2, 100, 12345, "chromium.org"};
   MockDnsClientRuleList rules;
-  rules.emplace_back("host", dns_protocol::kTypeSRV,
+  rules.emplace_back("host", dns_protocol::kTypeSRV, SecureDnsMode::AUTOMATIC,
                      MockDnsClientRule::Result(BuildTestDnsResponse(
                          "host", {kRecord1, kRecord2, kRecord3, kRecord4})),
                      false /* delay */);
diff --git a/net/dns/mapped_host_resolver.cc b/net/dns/mapped_host_resolver.cc
index a0f1339..04667430 100644
--- a/net/dns/mapped_host_resolver.cc
+++ b/net/dns/mapped_host_resolver.cc
@@ -80,8 +80,9 @@
 
 bool MappedHostResolver::HasCached(base::StringPiece hostname,
                                    HostCache::Entry::Source* source_out,
-                                   HostCache::EntryStaleness* stale_out) const {
-  return impl_->HasCached(hostname, source_out, stale_out);
+                                   HostCache::EntryStaleness* stale_out,
+                                   bool* secure_out) const {
+  return impl_->HasCached(hostname, source_out, stale_out, secure_out);
 }
 
 std::unique_ptr<base::Value> MappedHostResolver::GetDnsConfigAsValue() const {
diff --git a/net/dns/mapped_host_resolver.h b/net/dns/mapped_host_resolver.h
index f3968c65..a3b5872 100644
--- a/net/dns/mapped_host_resolver.h
+++ b/net/dns/mapped_host_resolver.h
@@ -57,7 +57,8 @@
   HostCache* GetHostCache() override;
   bool HasCached(base::StringPiece hostname,
                  HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out) const override;
+                 HostCache::EntryStaleness* stale_out,
+                 bool* secure_out) const override;
   std::unique_ptr<base::Value> GetDnsConfigAsValue() const override;
   void SetNoIPv6OnWifi(bool no_ipv6_on_wifi) override;
   bool GetNoIPv6OnWifi() override;
diff --git a/net/dns/mock_host_resolver.cc b/net/dns/mock_host_resolver.cc
index 160f82e..0b61518c 100644
--- a/net/dns/mock_host_resolver.cc
+++ b/net/dns/mock_host_resolver.cc
@@ -288,14 +288,18 @@
   return cache_.get();
 }
 
-bool MockHostResolverBase::HasCached(
-    base::StringPiece hostname,
-    HostCache::Entry::Source* source_out,
-    HostCache::EntryStaleness* stale_out) const {
+bool MockHostResolverBase::HasCached(base::StringPiece hostname,
+                                     HostCache::Entry::Source* source_out,
+                                     HostCache::EntryStaleness* stale_out,
+                                     bool* secure_out) const {
   if (!cache_)
     return false;
 
-  return !!cache_->GetMatchingKey(hostname, source_out, stale_out);
+  const HostCache::Key* key =
+      cache_->GetMatchingKey(hostname, source_out, stale_out);
+  if (key && secure_out != nullptr)
+    *secure_out = key->secure;
+  return !!key;
 }
 
 int MockHostResolverBase::LoadIntoCache(
@@ -918,10 +922,10 @@
                                        is_local_only);
 }
 
-bool HangingHostResolver::HasCached(
-    base::StringPiece hostname,
-    HostCache::Entry::Source* source_out,
-    HostCache::EntryStaleness* stale_out) const {
+bool HangingHostResolver::HasCached(base::StringPiece hostname,
+                                    HostCache::Entry::Source* source_out,
+                                    HostCache::EntryStaleness* stale_out,
+                                    bool* secure_out) const {
   return false;
 }
 
diff --git a/net/dns/mock_host_resolver.h b/net/dns/mock_host_resolver.h
index 3b60b19..798a7e5 100644
--- a/net/dns/mock_host_resolver.h
+++ b/net/dns/mock_host_resolver.h
@@ -122,7 +122,8 @@
   HostCache* GetHostCache() override;
   bool HasCached(base::StringPiece hostname,
                  HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out) const override;
+                 HostCache::EntryStaleness* stale_out,
+                 bool* secure_out) const override;
   void SetDnsConfigOverrides(const DnsConfigOverrides& overrides) override {}
 
   // Preloads the cache with what would currently be the result of a request
@@ -414,7 +415,8 @@
       override;
   bool HasCached(base::StringPiece hostname,
                  HostCache::Entry::Source* source_out,
-                 HostCache::EntryStaleness* stale_out) const override;
+                 HostCache::EntryStaleness* stale_out,
+                 bool* secure_out) const override;
 
   // Use to detect cancellations since there's otherwise no externally-visible
   // differentiation between a cancelled and a hung task.
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index 786ff57..19eaa7e 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -199,7 +199,7 @@
       http_09_on_non_default_ports_enabled_(false),
       read_buf_(read_buffer),
       read_buf_unused_offset_(0),
-      response_header_start_offset_(-1),
+      response_header_start_offset_(std::string::npos),
       received_bytes_(0),
       sent_bytes_(0),
       response_(nullptr),
@@ -820,7 +820,7 @@
     // Accepting truncated headers over HTTPS is a potential security
     // vulnerability, so just return an error in that case.
     //
-    // If response_header_start_offset_ is -1, this may be a < 8
+    // If response_header_start_offset_ is std::string::npos, this may be a < 8
     // byte HTTP/0.9 response. However, accepting such a response over HTTPS
     // would allow a MITM to truncate an HTTP/1.x status line to look like a
     // short HTTP/0.9 response if the peer put a record boundary at the first 8
@@ -838,7 +838,7 @@
 
     // Parse things as well as we can and let the caller decide what to do.
     int end_offset;
-    if (response_header_start_offset_ >= 0) {
+    if (response_header_start_offset_ != std::string::npos) {
       // The response looks to be a truncated set of HTTP headers.
       io_state_ = STATE_READ_BODY_COMPLETE;
       end_offset = read_buf_->offset();
@@ -910,7 +910,7 @@
         // request so we return OK here, which lets the caller inspect the
         // response and reject it in the event that we're setting up a CONNECT
         // tunnel.
-        response_header_start_offset_ = -1;
+        response_header_start_offset_ = std::string::npos;
         response_body_length_ = -1;
         // Now waiting for the second set of headers to be read.
       } else {
@@ -935,23 +935,25 @@
 int HttpStreamParser::FindAndParseResponseHeaders(int new_bytes) {
   DCHECK_GT(new_bytes, 0);
   DCHECK_EQ(0, read_buf_unused_offset_);
-  int end_offset = -1;
+  size_t end_offset = std::string::npos;
 
   // Look for the start of the status line, if it hasn't been found yet.
-  if (response_header_start_offset_ < 0) {
+  if (response_header_start_offset_ == std::string::npos) {
     response_header_start_offset_ = HttpUtil::LocateStartOfStatusLine(
         read_buf_->StartOfBuffer(), read_buf_->offset());
   }
 
-  if (response_header_start_offset_ >= 0) {
+  if (response_header_start_offset_ != std::string::npos) {
     // LocateEndOfHeaders looks for two line breaks in a row (With or without
     // carriage returns). So the end of the headers includes at most the last 3
     // bytes of the buffer from the past read. This optimization avoids O(n^2)
     // performance in the case each read only returns a couple bytes. It's not
     // too important in production, but for fuzzers with memory instrumentation,
     // it's needed to avoid timing out.
-    int search_start = std::max(response_header_start_offset_,
-                                read_buf_->offset() - new_bytes - 3);
+    size_t lower_bound =
+        (base::ClampedNumeric<size_t>(read_buf_->offset()) - new_bytes - 3)
+            .RawValue();
+    size_t search_start = std::max(response_header_start_offset_, lower_bound);
     end_offset = HttpUtil::LocateEndOfHeaders(
         read_buf_->StartOfBuffer(), read_buf_->offset(), search_start);
   } else if (read_buf_->offset() >= 8) {
@@ -960,7 +962,7 @@
     end_offset = 0;
   }
 
-  if (end_offset == -1)
+  if (end_offset == std::string::npos)
     return -1;
 
   int rv = ParseResponseHeaders(end_offset);
@@ -973,7 +975,7 @@
   scoped_refptr<HttpResponseHeaders> headers;
   DCHECK_EQ(0, read_buf_unused_offset_);
 
-  if (response_header_start_offset_ >= 0) {
+  if (response_header_start_offset_ != std::string::npos) {
     received_bytes_ += end_offset;
     headers = HttpResponseHeaders::TryToCreate(
         base::StringPiece(read_buf_->StartOfBuffer(), end_offset));
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index 6ca4b5b5..c09a709 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -228,8 +228,8 @@
   int read_buf_unused_offset_;
 
   // The amount beyond |read_buf_unused_offset_| where the status line starts;
-  // -1 if not found yet.
-  int response_header_start_offset_;
+  // std::string::npos if not found yet.
+  size_t response_header_start_offset_;
 
   // The amount of received data.  If connection is reused then intermediate
   // value may be bigger than final.
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 2baf637..6d9c6e63 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -641,27 +641,27 @@
 
 // Find the "http" substring in a status line. This allows for
 // some slop at the start. If the "http" string could not be found
-// then returns -1.
+// then returns std::string::npos.
 // static
-int HttpUtil::LocateStartOfStatusLine(const char* buf, int buf_len) {
-  const int slop = 4;
-  const int http_len = 4;
+size_t HttpUtil::LocateStartOfStatusLine(const char* buf, size_t buf_len) {
+  const size_t slop = 4;
+  const size_t http_len = 4;
 
   if (buf_len >= http_len) {
-    int i_max = std::min(buf_len - http_len, slop);
-    for (int i = 0; i <= i_max; ++i) {
+    size_t i_max = std::min(buf_len - http_len, slop);
+    for (size_t i = 0; i <= i_max; ++i) {
       if (base::LowerCaseEqualsASCII(base::StringPiece(buf + i, http_len),
                                      "http"))
         return i;
     }
   }
-  return -1;  // Not found
+  return std::string::npos;  // Not found
 }
 
-static int LocateEndOfHeadersHelper(const char* buf,
-                                    int buf_len,
-                                    int i,
-                                    bool accept_empty_header_list) {
+static size_t LocateEndOfHeadersHelper(const char* buf,
+                                       size_t buf_len,
+                                       size_t i,
+                                       bool accept_empty_header_list) {
   char last_c = '\0';
   bool was_lf = false;
   if (accept_empty_header_list) {
@@ -682,16 +682,16 @@
     }
     last_c = c;
   }
-  return -1;
+  return std::string::npos;
 }
 
-int HttpUtil::LocateEndOfAdditionalHeaders(const char* buf,
-                                           int buf_len,
-                                           int i) {
+size_t HttpUtil::LocateEndOfAdditionalHeaders(const char* buf,
+                                              size_t buf_len,
+                                              size_t i) {
   return LocateEndOfHeadersHelper(buf, buf_len, i, true);
 }
 
-int HttpUtil::LocateEndOfHeaders(const char* buf, int buf_len, int i) {
+size_t HttpUtil::LocateEndOfHeaders(const char* buf, size_t buf_len, size_t i) {
   return LocateEndOfHeadersHelper(buf, buf_len, i, false);
 }
 
@@ -738,7 +738,7 @@
 }
 
 std::string HttpUtil::AssembleRawHeaders(const char* input_begin,
-                                         int input_len) {
+                                         size_t input_len) {
   std::string raw_headers;
   raw_headers.reserve(input_len);
 
@@ -746,8 +746,8 @@
 
   // Skip any leading slop, since the consumers of this output
   // (HttpResponseHeaders) don't deal with it.
-  int status_begin_offset = LocateStartOfStatusLine(input_begin, input_len);
-  if (status_begin_offset != -1)
+  size_t status_begin_offset = LocateStartOfStatusLine(input_begin, input_len);
+  if (status_begin_offset != std::string::npos)
     input_begin += status_begin_offset;
 
   // Copy the status line.
diff --git a/net/http/http_util.h b/net/http/http_util.h
index bb34abc6..c757bf18 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -161,30 +161,29 @@
   // The reverse of Unquote() -- escapes and surrounds with "
   static std::string Quote(const std::string& str);
 
-  // Returns the start of the status line, or -1 if no status line was found.
-  // This allows for 4 bytes of junk to precede the status line (which is what
-  // mozilla does too).
-  // TODO(921389): Convert this to return size_t.
-  static int LocateStartOfStatusLine(const char* buf, int buf_len);
+  // Returns the start of the status line, or std::string::npos if no status
+  // line was found. This allows for 4 bytes of junk to precede the status line
+  // (which is what Mozilla does too).
+  static size_t LocateStartOfStatusLine(const char* buf, size_t buf_len);
 
-  // Returns index beyond the end-of-headers marker or -1 if not found.  RFC
-  // 2616 defines the end-of-headers marker as a double CRLF; however, some
-  // servers only send back LFs (e.g., Unix-based CGI scripts written using the
-  // ASIS Apache module).  This function therefore accepts the pattern LF[CR]LF
-  // as end-of-headers (just like Mozilla). The first line of |buf| is
-  // considered the status line, even if empty.
-  // The parameter |i| is the offset within |buf| to begin searching from.
-  // TODO(921389): Convert this to return size_t.
-  static int LocateEndOfHeaders(const char* buf, int buf_len, int i = 0);
+  // Returns index beyond the end-of-headers marker or std::string::npos if not
+  // found.  RFC 2616 defines the end-of-headers marker as a double CRLF;
+  // however, some servers only send back LFs (e.g., Unix-based CGI scripts
+  // written using the ASIS Apache module).  This function therefore accepts the
+  // pattern LF[CR]LF as end-of-headers (just like Mozilla). The first line of
+  // |buf| is considered the status line, even if empty. The parameter |i| is
+  // the offset within |buf| to begin searching from.
+  static size_t LocateEndOfHeaders(const char* buf,
+                                   size_t buf_len,
+                                   size_t i = 0);
 
   // Same as |LocateEndOfHeaders|, but does not expect a status line, so can be
   // used on multi-part responses or HTTP/1.x trailers.  As a result, if |buf|
   // starts with a single [CR]LF,  it is considered an empty header list, as
   // opposed to an empty status line above a header list.
-  // TODO(921389): Convert this to return size_t.
-  static int LocateEndOfAdditionalHeaders(const char* buf,
-                                          int buf_len,
-                                          int i = 0);
+  static size_t LocateEndOfAdditionalHeaders(const char* buf,
+                                             size_t buf_len,
+                                             size_t i = 0);
 
   // Assemble "raw headers" in the format required by HttpResponseHeaders.
   // This involves normalizing line terminators, converting [CR]LF to \0 and
@@ -197,7 +196,7 @@
   //
   // TODO(crbug.com/671799): Should remove or internalize this to
   //                         HttpResponseHeaders.
-  static std::string AssembleRawHeaders(const char* buf, int buf_len);
+  static std::string AssembleRawHeaders(const char* buf, size_t buf_len);
 
   // Converts assembled "raw headers" back to the HTTP response format. That is
   // convert each \0 occurence to CRLF. This is used by DevTools.
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index 53eefb4..0ddf34d 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -327,12 +327,12 @@
 TEST(HttpUtilTest, LocateEndOfHeaders) {
   struct {
     const char* const input;
-    int expected_result;
+    size_t expected_result;
   } tests[] = {
-      {"\r\n", -1},
-      {"\n", -1},
-      {"\r", -1},
-      {"foo", -1},
+      {"\r\n", std::string::npos},
+      {"\n", std::string::npos},
+      {"\r", std::string::npos},
+      {"foo", std::string::npos},
       {"\r\n\r\n", 4},
       {"foo\r\nbar\r\n\r\n", 12},
       {"foo\nbar\n\n", 9},
@@ -343,7 +343,7 @@
   };
   for (size_t i = 0; i < base::size(tests); ++i) {
     size_t input_len = strlen(tests[i].input);
-    int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len);
+    size_t eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len);
     EXPECT_EQ(tests[i].expected_result, eoh);
   }
 }
@@ -351,12 +351,12 @@
 TEST(HttpUtilTest, LocateEndOfAdditionalHeaders) {
   struct {
     const char* const input;
-    int expected_result;
+    size_t expected_result;
   } tests[] = {
       {"\r\n", 2},
       {"\n", 1},
-      {"\r", -1},
-      {"foo", -1},
+      {"\r", std::string::npos},
+      {"foo", std::string::npos},
       {"\r\n\r\n", 2},
       {"foo\r\nbar\r\n\r\n", 12},
       {"foo\nbar\n\n", 9},
@@ -367,7 +367,8 @@
   };
   for (size_t i = 0; i < base::size(tests); ++i) {
     size_t input_len = strlen(tests[i].input);
-    int eoh = HttpUtil::LocateEndOfAdditionalHeaders(tests[i].input, input_len);
+    size_t eoh =
+        HttpUtil::LocateEndOfAdditionalHeaders(tests[i].input, input_len);
     EXPECT_EQ(tests[i].expected_result, eoh);
   }
 }
diff --git a/net/server/http_server_unittest.cc b/net/server/http_server_unittest.cc
index 1b63016..19faad0 100644
--- a/net/server/http_server_unittest.cc
+++ b/net/server/http_server_unittest.cc
@@ -149,9 +149,9 @@
 
   bool IsCompleteResponse(const std::string& response) {
     // Check end of headers first.
-    int end_of_headers = HttpUtil::LocateEndOfHeaders(response.data(),
-                                                      response.size());
-    if (end_of_headers < 0)
+    size_t end_of_headers =
+        HttpUtil::LocateEndOfHeaders(response.data(), response.size());
+    if (end_of_headers == std::string::npos)
       return false;
 
     // Return true if response has data equal to or more than content length.
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 54792fb..28d0e68 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -196,32 +196,7 @@
       max_size(0) {}
 URLRequestContextBuilder::HttpCacheParams::~HttpCacheParams() = default;
 
-URLRequestContextBuilder::URLRequestContextBuilder()
-    : enable_brotli_(false),
-      network_quality_estimator_(nullptr),
-      data_enabled_(false),
-#if !BUILDFLAG(DISABLE_FILE_SUPPORT)
-      file_enabled_(false),
-#endif
-#if !BUILDFLAG(DISABLE_FTP_SUPPORT)
-      ftp_enabled_(false),
-#endif
-      http_cache_enabled_(true),
-      throttling_enabled_(false),
-      cookie_store_set_by_client_(false),
-      net_log_(nullptr),
-      shared_host_resolver_(nullptr),
-      pac_quick_check_enabled_(true),
-      pac_sanitize_url_policy_(ProxyResolutionService::SanitizeUrlPolicy::SAFE),
-      shared_proxy_delegate_(nullptr),
-      shared_http_auth_handler_factory_(nullptr),
-#if BUILDFLAG(ENABLE_REPORTING)
-      shared_cert_verifier_(nullptr),
-      network_error_logging_enabled_(false) {
-#else   // !BUILDFLAG(ENABLE_REPORTING)
-      shared_cert_verifier_(nullptr){
-#endif  // !BUILDFLAG(ENABLE_REPORTING)
-}
+URLRequestContextBuilder::URLRequestContextBuilder() = default;
 
 URLRequestContextBuilder::~URLRequestContextBuilder() = default;
 
@@ -536,8 +511,10 @@
         std::move(proxy_config_service_), context.get(),
         context->host_resolver(), context->network_delegate(),
         context->net_log());
-    proxy_resolution_service_->set_quick_check_enabled(pac_quick_check_enabled_);
-    proxy_resolution_service_->set_sanitize_url_policy(pac_sanitize_url_policy_);
+    proxy_resolution_service_->set_quick_check_enabled(
+        pac_quick_check_enabled_);
+    proxy_resolution_service_->set_sanitize_url_policy(
+        pac_sanitize_url_policy_);
   }
   ProxyResolutionService* proxy_resolution_service =
       proxy_resolution_service_.get();
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index f68548feb..70c424b 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -372,53 +372,54 @@
 
  private:
   std::string name_;
-  bool enable_brotli_;
-  NetworkQualityEstimator* network_quality_estimator_;
+  bool enable_brotli_ = false;
+  NetworkQualityEstimator* network_quality_estimator_ = nullptr;
 
   std::string accept_language_;
   std::string user_agent_;
   std::unique_ptr<HttpUserAgentSettings> http_user_agent_settings_;
 
   // Include support for data:// requests.
-  bool data_enabled_;
+  bool data_enabled_ = false;
 #if !BUILDFLAG(DISABLE_FILE_SUPPORT)
   // Include support for file:// requests.
-  bool file_enabled_;
+  bool file_enabled_ = false;
 #endif
 #if !BUILDFLAG(DISABLE_FTP_SUPPORT)
   // Include support for ftp:// requests.
-  bool ftp_enabled_;
+  bool ftp_enabled_ = false;
 #endif
-  bool http_cache_enabled_;
-  bool throttling_enabled_;
-  bool cookie_store_set_by_client_;
+  bool http_cache_enabled_ = true;
+  bool throttling_enabled_ = false;
+  bool cookie_store_set_by_client_ = false;
 
   HttpCacheParams http_cache_params_;
   HttpNetworkSession::Params http_network_session_params_;
   CreateHttpTransactionFactoryCallback create_http_network_transaction_factory_;
   base::FilePath transport_security_persister_path_;
-  NetLog* net_log_;
+  NetLog* net_log_ = nullptr;
   std::unique_ptr<HostResolver> host_resolver_;
-  HostResolver* shared_host_resolver_;
+  HostResolver* shared_host_resolver_ = nullptr;
   std::unique_ptr<ProxyConfigService> proxy_config_service_;
-  bool pac_quick_check_enabled_;
-  ProxyResolutionService::SanitizeUrlPolicy pac_sanitize_url_policy_;
+  bool pac_quick_check_enabled_ = true;
+  ProxyResolutionService::SanitizeUrlPolicy pac_sanitize_url_policy_ =
+      ProxyResolutionService::SanitizeUrlPolicy::SAFE;
   std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
   std::unique_ptr<SSLConfigService> ssl_config_service_;
   std::unique_ptr<NetworkDelegate> network_delegate_;
   CreateLayeredNetworkDelegate create_layered_network_delegate_callback_;
   std::unique_ptr<ProxyDelegate> proxy_delegate_;
-  ProxyDelegate* shared_proxy_delegate_;
+  ProxyDelegate* shared_proxy_delegate_ = nullptr;
   std::unique_ptr<CookieStore> cookie_store_;
   std::unique_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_;
-  HttpAuthHandlerFactory* shared_http_auth_handler_factory_;
+  HttpAuthHandlerFactory* shared_http_auth_handler_factory_ = nullptr;
   std::unique_ptr<CertVerifier> cert_verifier_;
-  CertVerifier* shared_cert_verifier_;
+  CertVerifier* shared_cert_verifier_ = nullptr;
   std::unique_ptr<CTVerifier> ct_verifier_;
   std::unique_ptr<CTPolicyEnforcer> ct_policy_enforcer_;
 #if BUILDFLAG(ENABLE_REPORTING)
   std::unique_ptr<ReportingPolicy> reporting_policy_;
-  bool network_error_logging_enabled_;
+  bool network_error_logging_enabled_ = false;
 #endif  // BUILDFLAG(ENABLE_REPORTING)
   std::vector<std::unique_ptr<URLRequestInterceptor>> url_request_interceptors_;
   CreateInterceptingJobFactory create_intercepting_job_factory_;
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 4db5cd4..f7242db5 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -91,6 +91,7 @@
 constexpr char kJSMetadataType[] = "metadata";
 constexpr char kJSBookmarks[] = "bookmarks";
 constexpr char kJSTitle[] = "title";
+constexpr char kJSCanSerializeDocument[] = "canSerializeDocument";
 // Get password (Plugin -> Page)
 constexpr char kJSGetPasswordType[] = "getPassword";
 // Get password complete arguments (Page -> Plugin)
@@ -1654,6 +1655,9 @@
     metadata_message.Set(pp::Var(kJSTitle), pp::Var(title));
     HistogramEnumeration("PDF.DocumentFeature", HAS_TITLE, FEATURES_COUNT);
   }
+  metadata_message.Set(
+      pp::Var(kJSCanSerializeDocument),
+      pp::Var(engine_->GetLoadedByteSize() <= kMaximumSavedFileSize));
 
   pp::VarArray bookmarks = engine_->GetBookmarks();
   metadata_message.Set(pp::Var(kJSBookmarks), bookmarks);
diff --git a/services/identity/identity_accessor_impl.cc b/services/identity/identity_accessor_impl.cc
index d4b28708..d6faa858 100644
--- a/services/identity/identity_accessor_impl.cc
+++ b/services/identity/identity_accessor_impl.cc
@@ -19,34 +19,30 @@
     const ScopeSet& scopes,
     const std::string& consumer_id,
     GetAccessTokenCallback consumer_callback,
-    ProfileOAuth2TokenService* token_service,
     IdentityAccessorImpl* manager)
-    : OAuth2TokenService::Consumer(consumer_id),
-      token_service_(token_service),
-      consumer_callback_(std::move(consumer_callback)),
-      manager_(manager) {
-  token_service_request_ =
-      token_service_->StartRequest(account_id, scopes, this);
+    : consumer_callback_(std::move(consumer_callback)), manager_(manager) {
+  access_token_fetcher_ =
+      manager->identity_manager_->CreateAccessTokenFetcherForAccount(
+          account_id, consumer_id, scopes,
+          base::BindOnce(&AccessTokenRequest::OnTokenRequestCompleted,
+                         base::Unretained(this)),
+          identity::AccessTokenFetcher::Mode::kImmediate);
 }
 
 IdentityAccessorImpl::AccessTokenRequest::~AccessTokenRequest() = default;
 
-void IdentityAccessorImpl::AccessTokenRequest::OnGetTokenSuccess(
-    const OAuth2TokenService::Request* request,
-    const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
-  OnRequestCompleted(request, token_response.access_token,
-                     token_response.expiration_time,
-                     GoogleServiceAuthError::AuthErrorNone());
-}
-
-void IdentityAccessorImpl::AccessTokenRequest::OnGetTokenFailure(
-    const OAuth2TokenService::Request* request,
-    const GoogleServiceAuthError& error) {
-  OnRequestCompleted(request, base::nullopt, base::Time(), error);
+void IdentityAccessorImpl::AccessTokenRequest::OnTokenRequestCompleted(
+    GoogleServiceAuthError error,
+    AccessTokenInfo access_token_info) {
+  if (error.state() == GoogleServiceAuthError::NONE) {
+    OnRequestCompleted(access_token_info.token,
+                       access_token_info.expiration_time, error);
+  } else {
+    OnRequestCompleted(base::nullopt, base::Time(), error);
+  }
 }
 
 void IdentityAccessorImpl::AccessTokenRequest::OnRequestCompleted(
-    const OAuth2TokenService::Request* request,
     const base::Optional<std::string>& access_token,
     base::Time expiration_time,
     const GoogleServiceAuthError& error) {
@@ -59,21 +55,18 @@
 // static
 void IdentityAccessorImpl::Create(mojom::IdentityAccessorRequest request,
                                   IdentityManager* identity_manager,
-                                  AccountTrackerService* account_tracker,
-                                  ProfileOAuth2TokenService* token_service) {
+                                  AccountTrackerService* account_tracker) {
   new IdentityAccessorImpl(std::move(request), identity_manager,
-                           account_tracker, token_service);
+                           account_tracker);
 }
 
 IdentityAccessorImpl::IdentityAccessorImpl(
     mojom::IdentityAccessorRequest request,
     IdentityManager* identity_manager,
-    AccountTrackerService* account_tracker,
-    ProfileOAuth2TokenService* token_service)
+    AccountTrackerService* account_tracker)
     : binding_(this, std::move(request)),
       identity_manager_(identity_manager),
-      account_tracker_(account_tracker),
-      token_service_(token_service) {
+      account_tracker_(account_tracker) {
   binding_.set_connection_error_handler(base::BindRepeating(
       &IdentityAccessorImpl::OnConnectionError, base::Unretained(this)));
 
@@ -98,7 +91,8 @@
   AccountState account_state = GetStateOfAccount(account_info);
 
   if (!account_state.has_refresh_token ||
-      token_service_->RefreshTokenHasError(account_info.account_id)) {
+      identity_manager_->GetErrorStateOfRefreshTokenForAccount(
+          account_info.account_id) != GoogleServiceAuthError::AuthErrorNone()) {
     primary_account_available_callbacks_.push_back(std::move(callback));
     return;
   }
@@ -123,8 +117,7 @@
                                           GetAccessTokenCallback callback) {
   std::unique_ptr<AccessTokenRequest> access_token_request =
       std::make_unique<AccessTokenRequest>(account_id, scopes, consumer_id,
-                                           std::move(callback), token_service_,
-                                           this);
+                                           std::move(callback), this);
 
   access_token_requests_[access_token_request.get()] =
       std::move(access_token_request);
@@ -147,7 +140,8 @@
   // Check whether the primary account is available and notify any waiting
   // consumers if so.
   if (account_state.is_primary_account && account_state.has_refresh_token &&
-      !token_service_->RefreshTokenHasError(account_info.account_id)) {
+      identity_manager_->GetErrorStateOfRefreshTokenForAccount(
+          account_info.account_id) == GoogleServiceAuthError::AuthErrorNone()) {
     DCHECK(!account_info.account_id.empty());
     DCHECK(!account_info.email.empty());
     DCHECK(!account_info.gaia.empty());
@@ -168,7 +162,7 @@
     const AccountInfo& account_info) {
   AccountState account_state;
   account_state.has_refresh_token =
-      token_service_->RefreshTokenIsAvailable(account_info.account_id);
+      identity_manager_->HasAccountWithRefreshToken(account_info.account_id);
   account_state.is_primary_account =
       (account_info.account_id == identity_manager_->GetPrimaryAccountId());
   return account_state;
diff --git a/services/identity/identity_accessor_impl.h b/services/identity/identity_accessor_impl.h
index 5200da7..b5ca967 100644
--- a/services/identity/identity_accessor_impl.h
+++ b/services/identity/identity_accessor_impl.h
@@ -25,44 +25,36 @@
  public:
   static void Create(mojom::IdentityAccessorRequest request,
                      IdentityManager* identity_manager,
-                     AccountTrackerService* account_tracker,
-                     ProfileOAuth2TokenService* token_service);
+                     AccountTrackerService* account_tracker);
 
   IdentityAccessorImpl(mojom::IdentityAccessorRequest request,
                        IdentityManager* identity_manager,
-                       AccountTrackerService* account_tracker,
-                       ProfileOAuth2TokenService* token_service);
+                       AccountTrackerService* account_tracker);
   ~IdentityAccessorImpl() override;
 
  private:
-  // Makes an access token request to the OAuth2TokenService on behalf of a
+  // Makes an access token request to the IdentityManager on behalf of a
   // given consumer that has made the request to the Identity Service.
-  class AccessTokenRequest : public OAuth2TokenService::Consumer {
+  class AccessTokenRequest {
    public:
     AccessTokenRequest(const std::string& account_id,
                        const ScopeSet& scopes,
                        const std::string& consumer_id,
                        GetAccessTokenCallback consumer_callback,
-                       ProfileOAuth2TokenService* token_service,
                        IdentityAccessorImpl* manager);
-    ~AccessTokenRequest() override;
+    ~AccessTokenRequest();
 
    private:
-    // OAuth2TokenService::Consumer:
-    void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
-                           const OAuth2AccessTokenConsumer::TokenResponse&
-                               token_response) override;
-    void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                           const GoogleServiceAuthError& error) override;
+    // Invoked after access token request completes (successful or not).
+    void OnTokenRequestCompleted(GoogleServiceAuthError error,
+                                 AccessTokenInfo access_token_info);
 
     // Completes the pending access token request by calling back the consumer.
-    void OnRequestCompleted(const OAuth2TokenService::Request* request,
-                            const base::Optional<std::string>& access_token,
+    void OnRequestCompleted(const base::Optional<std::string>& access_token,
                             base::Time expiration_time,
                             const GoogleServiceAuthError& error);
 
-    ProfileOAuth2TokenService* token_service_;
-    std::unique_ptr<OAuth2TokenService::Request> token_service_request_;
+    std::unique_ptr<AccessTokenFetcher> access_token_fetcher_;
     GetAccessTokenCallback consumer_callback_;
     IdentityAccessorImpl* manager_;
   };
@@ -103,7 +95,6 @@
   mojo::Binding<mojom::IdentityAccessor> binding_;
   IdentityManager* identity_manager_;
   AccountTrackerService* account_tracker_;
-  ProfileOAuth2TokenService* token_service_;
 
   // The set of pending requests for access tokens.
   AccessTokenRequests access_token_requests_;
diff --git a/services/identity/identity_accessor_impl_unittest.cc b/services/identity/identity_accessor_impl_unittest.cc
index cdea6e5f..0a70b55 100644
--- a/services/identity/identity_accessor_impl_unittest.cc
+++ b/services/identity/identity_accessor_impl_unittest.cc
@@ -64,7 +64,6 @@
         service_(
             identity_test_environment_.identity_manager(),
             &account_tracker_,
-            &token_service_,
             test_connector_factory_.RegisterInstance(mojom::kServiceName)) {
     AccountTrackerService::RegisterPrefs(pref_service_.registry());
     AccountFetcherService::RegisterPrefs(pref_service_.registry());
diff --git a/services/identity/identity_service.cc b/services/identity/identity_service.cc
index b9d85bc..41fff98 100644
--- a/services/identity/identity_service.cc
+++ b/services/identity/identity_service.cc
@@ -14,12 +14,10 @@
 
 IdentityService::IdentityService(IdentityManager* identity_manager,
                                  AccountTrackerService* account_tracker,
-                                 ProfileOAuth2TokenService* token_service,
                                  service_manager::mojom::ServiceRequest request)
     : service_binding_(this, std::move(request)),
       identity_manager_(identity_manager),
-      account_tracker_(account_tracker),
-      token_service_(token_service) {
+      account_tracker_(account_tracker) {
   registry_.AddInterface<mojom::IdentityAccessor>(
       base::Bind(&IdentityService::Create, base::Unretained(this)));
 }
@@ -40,7 +38,6 @@
     return;
 
   identity_manager_ = nullptr;
-  token_service_ = nullptr;
   account_tracker_ = nullptr;
 }
 
@@ -54,7 +51,7 @@
     return;
 
   IdentityAccessorImpl::Create(std::move(request), identity_manager_,
-                               account_tracker_, token_service_);
+                               account_tracker_);
 }
 
 }  // namespace identity
diff --git a/services/identity/identity_service.h b/services/identity/identity_service.h
index 3211bf4..c599fef 100644
--- a/services/identity/identity_service.h
+++ b/services/identity/identity_service.h
@@ -14,7 +14,6 @@
 #include "services/service_manager/public/mojom/service.mojom.h"
 
 class AccountTrackerService;
-class ProfileOAuth2TokenService;
 
 namespace identity {
 
@@ -22,7 +21,6 @@
  public:
   IdentityService(IdentityManager* identity_manager,
                   AccountTrackerService* account_tracker,
-                  ProfileOAuth2TokenService* token_service,
                   service_manager::mojom::ServiceRequest request);
   ~IdentityService() override;
 
@@ -44,7 +42,6 @@
 
   IdentityManager* identity_manager_;
   AccountTrackerService* account_tracker_;
-  ProfileOAuth2TokenService* token_service_;
 
   service_manager::BinderRegistry registry_;
 
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index 03afde7..7403d91 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -353,6 +353,12 @@
           token_service_->GetDelegate());
   return delegate->GetJavaObject();
 }
+
+void IdentityManager::ForceRefreshOfExtendedAccountInfo(
+    const std::string& account_id) {
+  DCHECK(HasAccountWithRefreshToken(account_id));
+  account_fetcher_service_->ForceRefreshOfAccountInfo(account_id);
+}
 #endif
 
 void IdentityManager::AddObserver(Observer* observer) {
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index 5965392..4e905ed 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -451,6 +451,12 @@
   // OAuth2TokenService.java has no more client usage.
   base::android::ScopedJavaLocalRef<jobject>
   LegacyGetOAuth2TokenServiceJavaObject();
+
+  // This method has the contractual assumption that the account is a known
+  // account and has as its semantics that it fetches the account info for the
+  // account, triggering an OnExtendedAccountInfoUpdated() callback if the info
+  // was successfully fetched.
+  void ForceRefreshOfExtendedAccountInfo(const std::string& refresh_token);
 #endif
 
   // Methods to register or remove observers.
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index 2daeb37..dda2d92 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -52,6 +52,14 @@
 const char kTestEmail2[] = "me2@gmail.com";
 const char kTestEmail3[] = "me3@gmail.com";
 
+#if defined(OS_ANDROID)
+const char kTestHostedDomain[] = "example.com";
+const char kTestFullName[] = "full_name";
+const char kTestGivenName[] = "given_name";
+const char kTestLocale[] = "locale";
+const char kTestPictureUrl[] = "http://picture.example.com/picture.jpg";
+#endif
+
 #if defined(OS_CHROMEOS)
 const char kTestEmailWithPeriod[] = "m.e@gmail.com";
 #endif
@@ -2491,4 +2499,37 @@
   EXPECT_EQ(account_info.account_id, extended_account_info.value().account_id);
 }
 
+#if defined(OS_ANDROID)
+TEST_F(IdentityManagerTest, ForceRefreshOfExtendedAccountInfo) {
+  account_fetcher()->OnNetworkInitialized();
+  AccountInfo account_info =
+      MakeAccountAvailable(identity_manager(), kTestEmail2);
+
+  identity_manager()->ForceRefreshOfExtendedAccountInfo(
+      account_info.account_id);
+
+  base::DictionaryValue user_info;
+  user_info.SetString("id", account_info.account_id);
+  user_info.SetString("email", account_info.email);
+  user_info.SetString("hd", kTestHostedDomain);
+  user_info.SetString("name", kTestFullName);
+  user_info.SetString("given_name", kTestGivenName);
+  user_info.SetString("locale", kTestLocale);
+  user_info.SetString("picture", kTestPictureUrl);
+  account_tracker()->SetAccountInfoFromUserInfo(account_info.account_id,
+                                                &user_info);
+
+  const AccountInfo& refreshed_account_info =
+      identity_manager_observer()->AccountFromAccountUpdatedCallback();
+  EXPECT_EQ(account_info.account_id, refreshed_account_info.account_id);
+  EXPECT_EQ(account_info.email, refreshed_account_info.email);
+  EXPECT_EQ(account_info.gaia, refreshed_account_info.gaia);
+  EXPECT_EQ(kTestHostedDomain, refreshed_account_info.hosted_domain);
+  EXPECT_EQ(kTestFullName, refreshed_account_info.full_name);
+  EXPECT_EQ(kTestGivenName, refreshed_account_info.given_name);
+  EXPECT_EQ(kTestLocale, refreshed_account_info.locale);
+  EXPECT_EQ(kTestPictureUrl, refreshed_account_info.picture_url);
+}
+#endif
+
 }  // namespace identity
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index 3baaf03..d7aba44d 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -288,7 +288,7 @@
     FinalizeLoader();
 
     timing_info_.start_time = head.request_start;
-    timing_info_.finish_time = base::TimeTicks::Now();
+    timing_info_.response_end = base::TimeTicks::Now();
     timing_info_.alpn_negotiated_protocol = head.alpn_negotiated_protocol;
     timing_info_.connection_info = head.connection_info;
     auto timing_allow_origin =
diff --git a/services/network/host_resolver_unittest.cc b/services/network/host_resolver_unittest.cc
index 3980779d..d80a15a 100644
--- a/services/network/host_resolver_unittest.cc
+++ b/services/network/host_resolver_unittest.cc
@@ -1136,7 +1136,7 @@
   static const char* kTextRecords[] = {"foo", "bar", "more text"};
   net::MockDnsClientRuleList rules;
   rules.emplace_back(
-      "example.com", net::dns_protocol::kTypeTXT,
+      "example.com", net::dns_protocol::kTypeTXT, net::SecureDnsMode::AUTOMATIC,
       net::MockDnsClientRule::Result(net::BuildTestDnsResponse(
           "example.com", {std::vector<std::string>(std::begin(kTextRecords),
                                                    std::end(kTextRecords))})),
@@ -1175,7 +1175,7 @@
 TEST_F(HostResolverTest, HostResults) {
   net::MockDnsClientRuleList rules;
   rules.emplace_back(
-      "example.com", net::dns_protocol::kTypePTR,
+      "example.com", net::dns_protocol::kTypePTR, net::SecureDnsMode::AUTOMATIC,
       net::MockDnsClientRule::Result(net::BuildTestDnsPointerResponse(
           "example.com", {"google.com", "chromium.org"})),
       false /* delay */);
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index ede6e0cc..5eaceb3 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -3103,11 +3103,13 @@
   CHECK(result.AssignFromIPLiteral(kResult));
   net::MockDnsClientRuleList rules;
   rules.emplace_back(kQueryHostname, net::dns_protocol::kTypeA,
+                     net::SecureDnsMode::AUTOMATIC,
                      net::MockDnsClientRule::Result(
                          net::BuildTestDnsResponse(kQueryHostname, result)),
                      false /* delay */);
   rules.emplace_back(
       kQueryHostname, net::dns_protocol::kTypeAAAA,
+      net::SecureDnsMode::AUTOMATIC,
       net::MockDnsClientRule::Result(net::MockDnsClientRule::ResultType::EMPTY),
       false /* delay */);
   auto mock_dns_client =
diff --git a/services/network/public/cpp/cors/preflight_timing_info.cc b/services/network/public/cpp/cors/preflight_timing_info.cc
index 2691e8c9..c74f32a 100644
--- a/services/network/public/cpp/cors/preflight_timing_info.cc
+++ b/services/network/public/cpp/cors/preflight_timing_info.cc
@@ -14,7 +14,7 @@
 PreflightTimingInfo::~PreflightTimingInfo() = default;
 
 bool PreflightTimingInfo::operator==(const PreflightTimingInfo& rhs) const {
-  return start_time == rhs.start_time && finish_time == rhs.finish_time &&
+  return start_time == rhs.start_time && response_end == rhs.response_end &&
          alpn_negotiated_protocol == rhs.alpn_negotiated_protocol &&
          connection_info == rhs.connection_info &&
          timing_allow_origin == rhs.timing_allow_origin &&
diff --git a/services/network/public/cpp/cors/preflight_timing_info.h b/services/network/public/cpp/cors/preflight_timing_info.h
index a59c56f9..819c5d9f 100644
--- a/services/network/public/cpp/cors/preflight_timing_info.h
+++ b/services/network/public/cpp/cors/preflight_timing_info.h
@@ -26,7 +26,7 @@
   ~PreflightTimingInfo();
 
   base::TimeTicks start_time;
-  base::TimeTicks finish_time;
+  base::TimeTicks response_end;
   std::string alpn_negotiated_protocol;
   net::HttpResponseInfo::ConnectionInfo connection_info =
       net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN;
diff --git a/services/network/public/cpp/network_ipc_param_traits.h b/services/network/public/cpp/network_ipc_param_traits.h
index 7089b8b6..431ca3a 100644
--- a/services/network/public/cpp/network_ipc_param_traits.h
+++ b/services/network/public/cpp/network_ipc_param_traits.h
@@ -117,7 +117,7 @@
 
 IPC_STRUCT_TRAITS_BEGIN(network::cors::PreflightTimingInfo)
   IPC_STRUCT_TRAITS_MEMBER(start_time)
-  IPC_STRUCT_TRAITS_MEMBER(finish_time)
+  IPC_STRUCT_TRAITS_MEMBER(response_end)
   IPC_STRUCT_TRAITS_MEMBER(alpn_negotiated_protocol)
   IPC_STRUCT_TRAITS_MEMBER(connection_info)
   IPC_STRUCT_TRAITS_MEMBER(timing_allow_origin)
diff --git a/services/network/public/cpp/server/http_server_unittest.cc b/services/network/public/cpp/server/http_server_unittest.cc
index 2af5ec40..05facf1e 100644
--- a/services/network/public/cpp/server/http_server_unittest.cc
+++ b/services/network/public/cpp/server/http_server_unittest.cc
@@ -130,9 +130,9 @@
  private:
   bool IsCompleteResponse(const std::string& response) {
     // Check end of headers first.
-    int end_of_headers =
+    size_t end_of_headers =
         net::HttpUtil::LocateEndOfHeaders(response.data(), response.size());
-    if (end_of_headers < 0)
+    if (end_of_headers == std::string::npos)
       return false;
 
     // Return true if response has data equal to or more than content length.
diff --git a/services/tracing/perfetto/json_trace_exporter.cc b/services/tracing/perfetto/json_trace_exporter.cc
index 6d664c0..6e6b4af7 100644
--- a/services/tracing/perfetto/json_trace_exporter.cc
+++ b/services/tracing/perfetto/json_trace_exporter.cc
@@ -36,6 +36,8 @@
                                     bool has_more) {
   DCHECK(!packets.empty() || !has_more);
 
+  // TODO(eseckler): |label_filter_| seems broken for anything but
+  // "traceEvents" (e.g. "systemTraceEvents" will output invalid JSON).
   if (label_filter_.empty() && !has_output_json_preamble_) {
     out_ += "{\"traceEvents\":[";
     has_output_json_preamble_ = true;
@@ -77,10 +79,10 @@
         base::JSONWriter::Write(*metadata_, &json_value);
         out_ += json_value;
       }
-    }
 
-    // Finish the json object we started in the preamble.
-    out_ += "}";
+      // Finish the json object we started in the preamble.
+      out_ += "}";
+    }
   }
 
   // Send any remaining data. There is no harm issuing the callback with an
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 653ca22..ef9b08e 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -585,7 +585,7 @@
         "name": "non_network_service_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "shards": 10
+          "shards": 21
         },
         "test": "browser_tests"
       },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6db509e..6853b698 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -949,6 +949,16 @@
       },
     },
   },
+  'non_network_service_browser_tests': {
+    'modifications': {
+      # chromium.chromiumos
+      'linux-chromeos-dbg': {
+        'swarming': {
+          'shards': 21,
+        },
+      },
+    },
+  },
   'non_network_service_webkit_layout_tests' : {
     'remove_from': [
       'Linux Tests (dbg)(1)(32)', # 32-bit linux is unsupported
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index c0ed490..71447aef 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -400,9 +400,7 @@
   sources = [
     "$target_gen_dir/javascript_parser.proto",
   ]
-  deps = [
-    ":gen_javascript_parser_proto",
-  ]
+  proto_deps = [ ":gen_javascript_parser_proto" ]
   proto_out_dir = ""
 }
 
diff --git a/testing/libfuzzer/proto/BUILD.gn b/testing/libfuzzer/proto/BUILD.gn
index 5e62020..47747e3 100644
--- a/testing/libfuzzer/proto/BUILD.gn
+++ b/testing/libfuzzer/proto/BUILD.gn
@@ -4,15 +4,6 @@
 
 import("//third_party/protobuf/proto_library.gni")
 
-copy("json_proto_copy") {
-  sources = [
-    "json.proto",
-  ]
-  outputs = [
-    "$root_gen_dir" + "/testing/libfuzzer/proto/json.proto",
-  ]
-}
-
 proto_library("json_proto") {
   sources = [
     "json.proto",
@@ -21,9 +12,6 @@
   # This way json.pb.h header goes into "$root_gen_dir" directory precisely,
   # otherwise it goes into "$root_gen_dir" + "/testing/libfuzzer/proto/".
   proto_out_dir = ""
-  deps = [
-    ":json_proto_copy",
-  ]
 }
 
 source_set("json_proto_converter") {
diff --git a/third_party/blink/public/platform/web_resource_timing_info.h b/third_party/blink/public/platform/web_resource_timing_info.h
index 65e7f29..250ae8e 100644
--- a/third_party/blink/public/platform/web_resource_timing_info.h
+++ b/third_party/blink/public/platform/web_resource_timing_info.h
@@ -46,7 +46,7 @@
 
   WebURLLoadTiming timing;
   base::TimeTicks last_redirect_end_time;
-  base::TimeTicks finish_time;
+  base::TimeTicks response_end;
 
   uint64_t transfer_size;
   uint64_t encoded_body_size;
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_custom_xpath_ns_resolver.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_custom_xpath_ns_resolver.cc
index dc98870..6d9ada6 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_custom_xpath_ns_resolver.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_custom_xpath_ns_resolver.cc
@@ -68,7 +68,7 @@
     LocalFrame* frame = ToLocalFrameIfNotDetached(script_state_->GetContext());
     if (frame)
       frame->Console().AddMessage(ConsoleMessage::Create(
-          kJSMessageSource, kErrorMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kError,
           "XPathNSResolver does not have a lookupNamespaceURI method."));
     return g_null_atom;
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
index f48d9bb..2960df8 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_embedder_graph_builder.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
 #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
@@ -129,14 +130,30 @@
   return node;
 }
 
-}  // namespace
-
-class V8EmbedderGraphBuilder
+// V8EmbedderGraphBuilder is used to build heap snapshots of Blink's managed
+// object graph. On a high level, the following operations are performed:
+// - Objects are classified as attached, detached, or unknown.
+// - Depending an implicit mode, objects are classified as relevant or internal.
+//   This classification happens based on NameTrait and the fact that all
+//   ScriptWrappable objects (those that can have JS properties) are using that
+//   trait.
+// - Not relevant objects are filtered where possible, e.g., sub graphs of
+//   internals are filtered and not reported.
+//
+// The algorithm performs a single pass on the graph, starting from V8-to-Blink
+// references that identify attached nodes. Each object then starts a recursion
+// into its own subgraph to identify and filter subgraphs that only consist of
+// internals. Roots, which are potentially Blink only, are transitively
+// traversed after handling JavaScript related objects.
+class GC_PLUGIN_IGNORE(
+    "This class is not managed by Oilpan but GC plugin recognizes it as such "
+    "due to Trace methods.") V8EmbedderGraphBuilder
     : public Visitor,
       public v8::PersistentHandleVisitor,
       public v8::EmbedderHeapTracer::TracedGlobalHandleVisitor {
  public:
   V8EmbedderGraphBuilder(v8::Isolate*, Graph*, NodeBuilder*);
+  ~V8EmbedderGraphBuilder() override;
 
   void BuildEmbedderGraph();
 
@@ -150,15 +167,13 @@
 
   // Visitor overrides.
   void Visit(const TraceWrapperV8Reference<v8::Value>&) final;
-  void VisitWithWrappers(void*, TraceDescriptor) final;
+  void Visit(void*, TraceDescriptor) final;
   void VisitBackingStoreStrongly(void* object,
                                  void** object_slot,
                                  TraceDescriptor desc) final;
 
   // Unused Visitor overrides.
-  void Visit(void* object, TraceDescriptor desc) final {
-    // TODO(mlippautz): Implement for unified heap snapshots.
-  }
+  void VisitWithWrappers(void* object, TraceDescriptor desc) final {}
   void VisitWeak(void* object,
                  void** object_slot,
                  TraceDescriptor desc,
@@ -178,10 +193,9 @@
     STACK_ALLOCATED();
 
    public:
-    ParentScope(V8EmbedderGraphBuilder* visitor, EmbedderNode* parent)
+    ParentScope(V8EmbedderGraphBuilder* visitor, Traceable traceable)
         : visitor_(visitor) {
-      DCHECK_EQ(visitor->current_parent_, nullptr);
-      visitor->current_parent_ = parent;
+      visitor->current_parent_ = traceable;
     }
     ~ParentScope() { visitor_->current_parent_ = nullptr; }
 
@@ -189,38 +203,176 @@
     V8EmbedderGraphBuilder* const visitor_;
   };
 
-  struct WorklistItem {
-    EmbedderNode* node;
-    Traceable traceable;
-    TraceCallback trace_callback;
+  class State final {
+   public:
+    State(Traceable traceable, const char* name, DomTreeState dom_tree_state)
+        : traceable_(traceable), name_(name), dom_tree_state_(dom_tree_state) {}
+    explicit State(EmbedderNode* node)
+        : node_(node), dom_tree_state_(node->GetDomTreeState()) {}
+
+    bool IsVisited() const { return visited_; }
+    void MarkVisited() { visited_ = true; }
+
+    bool IsPending() const { return pending_; }
+    void MarkPending() { pending_ = true; }
+    void UnmarkPending() { pending_ = false; }
+
+    bool HasNode() const { return node_; }
+    EmbedderNode* GetOrCreateNode(NodeBuilder* builder) {
+      if (!node_) {
+        DCHECK(name_);
+        node_ = builder->GraphNode(traceable_, name_, nullptr, dom_tree_state_);
+      }
+      return node_;
+    }
+
+    DomTreeState GetDomTreeState() const { return dom_tree_state_; }
+    void UpdateDomTreeState(DomTreeState parent_dom_tree_state) {
+      // If the child's state is unknown, then take the parent's state.
+      // If the parent is attached, then the child is also attached.
+      if (dom_tree_state_ == DomTreeState::kUnknown ||
+          parent_dom_tree_state == DomTreeState::kAttached) {
+        dom_tree_state_ = parent_dom_tree_state;
+      }
+      if (node_)
+        node_->UpdateDomTreeState(dom_tree_state_);
+    }
+
+   private:
+    EmbedderNode* node_ = nullptr;
+    Traceable traceable_ = nullptr;
+    const char* name_ = nullptr;
+    DomTreeState dom_tree_state_;
+    bool visited_ = false;
+    bool pending_ = false;
   };
 
+  // WorklistItemBase is used for different kinds of items that require
+  // processing the regular worklist.
+  class WorklistItemBase {
+   public:
+    explicit WorklistItemBase(State* parent, State* to_process)
+        : parent_(parent), to_process_(to_process) {}
+    virtual ~WorklistItemBase() = default;
+    virtual void Process(V8EmbedderGraphBuilder*) = 0;
+
+    State* to_process() const { return to_process_; }
+    State* parent() const { return parent_; }
+
+   private:
+    State* const parent_;
+    State* const to_process_;
+  };
+
+  // A VisitationItem processes a given object and visits all its children.
+  class VisitationItem final : public WorklistItemBase {
+   public:
+    VisitationItem(State* parent,
+                   State* to_process,
+                   Traceable traceable,
+                   TraceCallback trace_callback)
+        : WorklistItemBase(parent, to_process),
+          traceable_(traceable),
+          trace_callback_(trace_callback) {}
+
+    void Process(V8EmbedderGraphBuilder* builder) final {
+      // Post-order traversal as parent needs the full information on child.
+      builder->worklist_.push_back(std::unique_ptr<WorklistItemBase>{
+          new VisitationDoneItem(parent(), to_process())});
+      to_process()->MarkPending();
+      DCHECK(to_process()->IsPending());
+      DCHECK(to_process()->IsVisited());
+      ParentScope parent_scope(builder, traceable_);
+      trace_callback_(builder, const_cast<void*>(traceable_));
+    }
+
+   private:
+    Traceable traceable_;
+    TraceCallback trace_callback_;
+  };
+
+  // A VisitationDoneItem unmarks the pending state of an object and creates an
+  // edge from a parent in case there is one.
+  class VisitationDoneItem final : public WorklistItemBase {
+   public:
+    VisitationDoneItem(State* parent, State* to_process)
+        : WorklistItemBase(parent, to_process) {}
+
+    void Process(V8EmbedderGraphBuilder* builder) final {
+      if (parent() && to_process()->HasNode()) {
+        NodeBuilder* node_builder = builder->node_builder_;
+        builder->graph_->AddEdge(parent()->GetOrCreateNode(node_builder),
+                                 to_process()->GetOrCreateNode(node_builder));
+      }
+      to_process()->UnmarkPending();
+    }
+  };
+
+  State* GetOrCreateState(Traceable traceable,
+                          const char* name,
+                          DomTreeState dom_tree_state) {
+    if (!states_.Contains(traceable)) {
+      states_.insert(traceable, new State(traceable, name, dom_tree_state));
+    }
+    return states_.at(traceable);
+  }
+
+  State* GetStateNotNull(Traceable traceable) {
+    CHECK(states_.Contains(traceable));
+    return states_.at(traceable);
+  }
+
+  State* EnsureState(Traceable traceable, EmbedderNode* node) {
+    if (!states_.Contains(traceable)) {
+      states_.insert(traceable, new State(node));
+    }
+    return states_.at(traceable);
+  }
+
+  void EnsureRootState(EmbedderNode* node) {
+    CHECK(!states_.Contains(node));
+    states_.insert(node, new State(node));
+  }
+
   void VisitPersistentHandleInternal(v8::Local<v8::Object>, uint16_t);
 
-  WorklistItem ToWorklistItem(EmbedderNode*, const TraceDescriptor&) const;
-
   void VisitPendingActivities();
+  void VisitBlinkRoots();
   void VisitTransitiveClosure();
 
-  // Push the item to the default worklist if item.traceable was not
+  // Push a VisitatonItem to the main worklist in case the State has not been
   // already visited.
-  void PushToWorklist(WorklistItem);
+  void CreateAndPushVisitationItem(State* parent,
+                                   State* to_process,
+                                   Traceable traceable,
+                                   TraceCallback trace_callback) {
+    DCHECK(!to_process->IsVisited());
+    to_process->MarkVisited();
+    worklist_.push_back(std::unique_ptr<WorklistItemBase>{
+        new VisitationItem(parent, to_process, traceable, trace_callback)});
+  }
+
+  void PushVisitationItem(std::unique_ptr<VisitationItem> item) {
+    if (!item->to_process()->IsVisited()) {
+      item->to_process()->MarkVisited();
+      worklist_.push_back(std::move(item));
+    }
+  }
 
   v8::Isolate* const isolate_;
   Graph* const graph_;
   NodeBuilder* const node_builder_;
 
-  EmbedderNode* current_parent_ = nullptr;
-
-  HashSet<Traceable> visited_;
+  Traceable current_parent_ = nullptr;
+  HashMap<Traceable, State*> states_;
   // The default worklist that is used to visit transitive closure.
-  Deque<WorklistItem> worklist_;
+  Deque<std::unique_ptr<WorklistItemBase>> worklist_;
   // The worklist that collects detached Nodes during persistent handle
   // iteration.
-  Deque<WorklistItem> detached_worklist_;
+  Deque<std::unique_ptr<VisitationItem>> detached_worklist_;
   // The worklist that collects ScriptWrappables with unknown information
   // about attached/detached state during persistent handle iteration.
-  Deque<WorklistItem> unknown_worklist_;
+  Deque<std::unique_ptr<VisitationItem>> unknown_worklist_;
 };
 
 V8EmbedderGraphBuilder::V8EmbedderGraphBuilder(v8::Isolate* isolate,
@@ -235,6 +387,12 @@
   CHECK_EQ(isolate, ThreadState::Current()->GetIsolate());
 }
 
+V8EmbedderGraphBuilder::~V8EmbedderGraphBuilder() {
+  for (const auto& kvp : states_) {
+    delete kvp.value;
+  }
+}
+
 void V8EmbedderGraphBuilder::BuildEmbedderGraph() {
   isolate_->VisitHandlesWithClassIds(this);
   v8::EmbedderHeapTracer* tracer =
@@ -244,14 +402,14 @@
 // At this point we collected ScriptWrappables in three groups:
 // attached, detached, and unknown.
 #if DCHECK_IS_ON()
-  for (const WorklistItem& item : worklist_) {
-    DCHECK_EQ(DomTreeState::kAttached, item.node->GetDomTreeState());
+  for (auto const& item : worklist_) {
+    DCHECK_EQ(DomTreeState::kAttached, item->to_process()->GetDomTreeState());
   }
-  for (const WorklistItem& item : detached_worklist_) {
-    DCHECK_EQ(DomTreeState::kDetached, item.node->GetDomTreeState());
+  for (auto const& item : detached_worklist_) {
+    DCHECK_EQ(DomTreeState::kDetached, item->to_process()->GetDomTreeState());
   }
-  for (const WorklistItem& item : unknown_worklist_) {
-    DCHECK_EQ(DomTreeState::kUnknown, item.node->GetDomTreeState());
+  for (auto const& item : unknown_worklist_) {
+    DCHECK_EQ(DomTreeState::kUnknown, item->to_process()->GetDomTreeState());
   }
 #endif
   // We need to propagate attached/detached information to ScriptWrappables
@@ -273,18 +431,20 @@
   VisitTransitiveClosure();
   // Stage 2: find transitive closure of the detached nodes.
   while (!detached_worklist_.empty()) {
-    auto item = detached_worklist_.back();
+    auto item = std::move(detached_worklist_.back());
     detached_worklist_.pop_back();
-    PushToWorklist(item);
+    PushVisitationItem(std::move(item));
   }
   VisitTransitiveClosure();
   // Stage 3: find transitive closure of the unknown nodes.
   // Nodes reachable only via pending activities are treated as unknown.
   VisitPendingActivities();
+  if (RuntimeEnabledFeatures::HeapUnifiedGarbageCollectionEnabled())
+    VisitBlinkRoots();
   while (!unknown_worklist_.empty()) {
-    auto item = unknown_worklist_.back();
+    auto item = std::move(unknown_worklist_.back());
     unknown_worklist_.pop_back();
-    PushToWorklist(item);
+    PushVisitationItem(std::move(item));
   }
   VisitTransitiveClosure();
   DCHECK(worklist_.empty());
@@ -303,18 +463,23 @@
       DomTreeStateFromWrapper(isolate_, class_id, v8_value);
   EmbedderNode* graph_node = node_builder_->GraphNode(
       traceable, traceable->NameInHeapSnapshot(), wrapper, dom_tree_state);
+  State* const to_process_state = EnsureState(traceable, graph_node);
   const TraceDescriptor& descriptor =
       TraceDescriptorFor<ScriptWrappable>(traceable);
-  WorklistItem item = ToWorklistItem(graph_node, descriptor);
   switch (graph_node->GetDomTreeState()) {
     case DomTreeState::kAttached:
-      PushToWorklist(item);
+      CreateAndPushVisitationItem(nullptr, to_process_state, traceable,
+                                  descriptor.callback);
       break;
     case DomTreeState::kDetached:
-      detached_worklist_.push_back(item);
+      detached_worklist_.push_back(
+          std::unique_ptr<VisitationItem>{new VisitationItem(
+              nullptr, to_process_state, traceable, descriptor.callback)});
       break;
     case DomTreeState::kUnknown:
-      unknown_worklist_.push_back(item);
+      unknown_worklist_.push_back(
+          std::unique_ptr<VisitationItem>{new VisitationItem(
+              nullptr, to_process_state, traceable, descriptor.callback)});
       break;
   }
 }
@@ -344,26 +509,49 @@
   // Add an edge from the current parent to the V8 object.
   v8::Local<v8::Value> v8_value = traced_wrapper.NewLocal(isolate_);
   if (!v8_value.IsEmpty()) {
-    graph_->AddEdge(current_parent_, node_builder_->GraphNode(v8_value));
+    State* parent = GetStateNotNull(current_parent_);
+    graph_->AddEdge(parent->GetOrCreateNode(node_builder_),
+                    node_builder_->GraphNode(v8_value));
   }
 }
 
-void V8EmbedderGraphBuilder::VisitWithWrappers(
-    void* object,
-    TraceDescriptor wrapper_descriptor) {
-  // Add an edge from the current parent to this object.
-  // Also push the object to the worklist in order to process its members.
+void V8EmbedderGraphBuilder::Visit(void* object,
+                                   TraceDescriptor wrapper_descriptor) {
   const void* traceable = wrapper_descriptor.base_object_payload;
-  const char* name =
-      GCInfoTable::Get()
-          .GCInfoFromIndex(
-              HeapObjectHeader::FromPayload(traceable)->GcInfoIndex())
-          ->name(traceable)
-          .value;
-  EmbedderNode* graph_node = node_builder_->GraphNode(
-      traceable, name, nullptr, current_parent_->GetDomTreeState());
-  graph_->AddEdge(current_parent_, graph_node);
-  PushToWorklist(ToWorklistItem(graph_node, wrapper_descriptor));
+  const GCInfo* info = GCInfoTable::Get().GCInfoFromIndex(
+      HeapObjectHeader::FromPayload(traceable)->GcInfoIndex());
+  HeapObjectName name = info->name(traceable);
+
+  State* const parent = GetStateNotNull(current_parent_);
+  State* const current =
+      GetOrCreateState(traceable, name.value, parent->GetDomTreeState());
+  if (current->IsPending()) {
+    if (parent->HasNode()) {
+      // Backedge in currently processed graph.
+      graph_->AddEdge(parent->GetOrCreateNode(node_builder_),
+                      current->GetOrCreateNode(node_builder_));
+    }
+    return;
+  }
+
+  // Immediately materialize the node if it is not hidden.
+  if (!name.name_is_hidden) {
+    current->GetOrCreateNode(node_builder_);
+  }
+
+  // Propagate the parent's DomTreeState down to the current state.
+  current->UpdateDomTreeState(parent->GetDomTreeState());
+
+  if (!current->IsVisited()) {
+    CreateAndPushVisitationItem(parent, current, traceable, info->trace);
+  } else {
+    // Edge into an already processed subgraph.
+    if (current->HasNode()) {
+      // Create an edge in case the current node has already been visited.
+      graph_->AddEdge(parent->GetOrCreateNode(node_builder_),
+                      current->GetOrCreateNode(node_builder_));
+    }
+  }
 }
 
 void V8EmbedderGraphBuilder::VisitBackingStoreStrongly(void* object,
@@ -379,33 +567,41 @@
   EmbedderNode* root =
       static_cast<EmbedderNode*>(graph_->AddNode(std::unique_ptr<Graph::Node>(
           new EmbedderRootNode("Pending activities"))));
+  EnsureRootState(root);
   ParentScope parent(this, root);
   ActiveScriptWrappableBase::TraceActiveScriptWrappables(isolate_, this);
 }
 
-V8EmbedderGraphBuilder::WorklistItem V8EmbedderGraphBuilder::ToWorklistItem(
-    EmbedderNode* node,
-    const TraceDescriptor& descriptor) const {
-  return {node, descriptor.base_object_payload, descriptor.callback};
-}
-
-void V8EmbedderGraphBuilder::PushToWorklist(WorklistItem item) {
-  if (!visited_.Contains(item.traceable)) {
-    visited_.insert(item.traceable);
-    worklist_.push_back(item);
+void V8EmbedderGraphBuilder::VisitBlinkRoots() {
+  {
+    EmbedderNode* root = static_cast<EmbedderNode*>(graph_->AddNode(
+        std::unique_ptr<Graph::Node>(new EmbedderRootNode("Blink roots"))));
+    EnsureRootState(root);
+    ParentScope parent(this, root);
+    ThreadState::Current()->GetPersistentRegion()->TracePersistentNodes(this);
+  }
+  {
+    EmbedderNode* root =
+        static_cast<EmbedderNode*>(graph_->AddNode(std::unique_ptr<Graph::Node>(
+            new EmbedderRootNode("Blink cross-thread roots"))));
+    EnsureRootState(root);
+    ParentScope parent(this, root);
+    MutexLocker persistent_lock(ProcessHeap::CrossThreadPersistentMutex());
+    ProcessHeap::GetCrossThreadPersistentRegion().TracePersistentNodes(this);
   }
 }
 
 void V8EmbedderGraphBuilder::VisitTransitiveClosure() {
   // Depth-first search.
   while (!worklist_.empty()) {
-    auto item = worklist_.back();
+    std::unique_ptr<WorklistItemBase> item = std::move(worklist_.back());
     worklist_.pop_back();
-    ParentScope parent(this, item.node);
-    item.trace_callback(this, const_cast<void*>(item.traceable));
+    item->Process(this);
   }
 }
 
+}  // namespace
+
 void EmbedderGraphBuilder::BuildEmbedderGraphCallback(v8::Isolate* isolate,
                                                       v8::EmbedderGraph* graph,
                                                       void*) {
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 2aabade3..06154336 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -196,21 +196,21 @@
 }
 
 namespace {
-MessageLevel MessageLevelFromNonFatalErrorLevel(int error_level) {
-  MessageLevel level = kErrorMessageLevel;
+mojom::ConsoleMessageLevel MessageLevelFromNonFatalErrorLevel(int error_level) {
+  mojom::ConsoleMessageLevel level = mojom::ConsoleMessageLevel::kError;
   switch (error_level) {
     case v8::Isolate::kMessageDebug:
-      level = kVerboseMessageLevel;
+      level = mojom::ConsoleMessageLevel::kVerbose;
       break;
     case v8::Isolate::kMessageLog:
     case v8::Isolate::kMessageInfo:
-      level = kInfoMessageLevel;
+      level = mojom::ConsoleMessageLevel::kInfo;
       break;
     case v8::Isolate::kMessageWarning:
-      level = kWarningMessageLevel;
+      level = mojom::ConsoleMessageLevel::kWarning;
       break;
     case v8::Isolate::kMessageError:
-      level = kInfoMessageLevel;
+      level = mojom::ConsoleMessageLevel::kInfo;
       break;
     default:
       NOTREACHED();
diff --git a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
index 2b30deb..bbfe849 100644
--- a/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/attributes.cc.tmpl
@@ -427,7 +427,7 @@
     if (!IsValidEnum(cpp_value, kValidValues, base::size(kValidValues),
                      "{{attribute.enum_type}}", dummy_exception_state)) {
       ExecutionContext::ForCurrentRealm(info)->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+          ConsoleMessage::Create(kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
                                  dummy_exception_state.Message()));
       return;
     }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
index c0ffad6..9cb8629 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -272,7 +272,7 @@
     if (!IsValidEnum(cpp_value, kValidValues, base::size(kValidValues),
                      "TestEnum", dummy_exception_state)) {
       ExecutionContext::ForCurrentRealm(info)->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+          ConsoleMessage::Create(kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
                                  dummy_exception_state.Message()));
       return;
     }
@@ -320,7 +320,7 @@
     if (!IsValidEnum(cpp_value, kValidValues, base::size(kValidValues),
                      "TestEnum", dummy_exception_state)) {
       ExecutionContext::ForCurrentRealm(info)->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+          ConsoleMessage::Create(kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
                                  dummy_exception_state.Message()));
       return;
     }
@@ -1292,7 +1292,7 @@
     if (!IsValidEnum(cpp_value, kValidValues, base::size(kValidValues),
                      "PartialEnumType", dummy_exception_state)) {
       ExecutionContext::ForCurrentRealm(info)->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+          ConsoleMessage::Create(kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
                                  dummy_exception_state.Message()));
       return;
     }
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index 3725cbd..a413aca 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -1553,7 +1553,7 @@
     if (!IsValidEnum(cpp_value, kValidValues, base::size(kValidValues),
                      "TestEnum", dummy_exception_state)) {
       ExecutionContext::ForCurrentRealm(info)->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+          ConsoleMessage::Create(kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
                                  dummy_exception_state.Message()));
       return;
     }
@@ -1601,7 +1601,7 @@
     if (!IsValidEnum(cpp_value, kValidValues, base::size(kValidValues),
                      "TestEnum", dummy_exception_state)) {
       ExecutionContext::ForCurrentRealm(info)->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+          ConsoleMessage::Create(kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
                                  dummy_exception_state.Message()));
       return;
     }
diff --git a/third_party/blink/renderer/core/animation/effect_input.cc b/third_party/blink/renderer/core/animation/effect_input.cc
index 89950ab..67af486 100644
--- a/third_party/blink/renderer/core/animation/effect_input.cc
+++ b/third_party/blink/renderer/core/animation/effect_input.cc
@@ -102,7 +102,7 @@
     if (!set_result.did_parse && execution_context) {
       if (document.GetFrame()) {
         document.GetFrame()->Console().AddMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Invalid keyframe value for property " + property + ": " + value));
       }
     }
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.cc
index 17085a5..4aabd90 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.cc
@@ -12,4 +12,11 @@
   return CSSKeywordValue::Create(keyword_value_);
 }
 
+bool CrossThreadKeywordValue::operator==(
+    const CrossThreadStyleValue& other) const {
+  if (auto* o = DynamicTo<CrossThreadKeywordValue>(other))
+    return keyword_value_ == o->keyword_value_;
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.h b/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.h
index 4d821cd..a235477 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.h
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_keyword_value.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -20,8 +21,13 @@
       : keyword_value_(keyword) {}
   ~CrossThreadKeywordValue() override = default;
 
+  StyleValueType GetType() const override {
+    return StyleValueType::kKeywordType;
+  }
   CSSStyleValue* ToCSSStyleValue() override;
 
+  bool operator==(const CrossThreadStyleValue&) const override;
+
  private:
   friend class CrossThreadStyleValueTest;
 
@@ -29,6 +35,14 @@
   DISALLOW_COPY_AND_ASSIGN(CrossThreadKeywordValue);
 };
 
+template <>
+struct DowncastTraits<CrossThreadKeywordValue> {
+  static bool AllowFrom(const CrossThreadStyleValue& keyword_value) {
+    return keyword_value.GetType() ==
+           CrossThreadStyleValue::StyleValueType::kKeywordType;
+  }
+};
+
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h
index 080a584..bd9fd5a 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h
@@ -16,10 +16,19 @@
 // passed cross threads.
 class CORE_EXPORT CrossThreadStyleValue {
  public:
+  enum class StyleValueType {
+    kUnknownType,
+    kKeywordType,
+    kUnitType,
+  };
+
   virtual ~CrossThreadStyleValue() = default;
 
+  virtual StyleValueType GetType() const = 0;
   virtual CSSStyleValue* ToCSSStyleValue() = 0;
 
+  virtual bool operator==(const CrossThreadStyleValue&) const = 0;
+
  protected:
   CrossThreadStyleValue() = default;
 
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
index 95bd0b92..e4a6458 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
@@ -165,4 +165,93 @@
   EXPECT_EQ(static_cast<CSSUnitValue*>(style_value)->unit(), "deg");
 }
 
+TEST_F(CrossThreadStyleValueTest, ComparingNullValues) {
+  // Two null values are equal to each other.
+  std::unique_ptr<CrossThreadStyleValue> null_value1(nullptr);
+  std::unique_ptr<CrossThreadStyleValue> null_value2(nullptr);
+  EXPECT_TRUE(DataEquivalent(null_value1, null_value2));
+
+  // If one argument is null and the other isn't they are never equal.
+  std::unique_ptr<CrossThreadStyleValue> keyword_value(
+      new CrossThreadKeywordValue("keyword"));
+  std::unique_ptr<CrossThreadStyleValue> unit_value(
+      new CrossThreadUnitValue(1, CSSPrimitiveValue::UnitType::kDegrees));
+  std::unique_ptr<CrossThreadStyleValue> unsupported_value(
+      new CrossThreadUnsupportedValue("unsupported"));
+
+  EXPECT_FALSE(DataEquivalent(null_value1, keyword_value));
+  EXPECT_FALSE(DataEquivalent(null_value1, unit_value));
+  EXPECT_FALSE(DataEquivalent(null_value1, unsupported_value));
+  EXPECT_FALSE(DataEquivalent(keyword_value, null_value1));
+  EXPECT_FALSE(DataEquivalent(unit_value, null_value1));
+  EXPECT_FALSE(DataEquivalent(unsupported_value, null_value1));
+}
+
+TEST_F(CrossThreadStyleValueTest, ComparingDifferentTypes) {
+  // Mismatching types are never equal.
+  std::unique_ptr<CrossThreadStyleValue> keyword_value(
+      new CrossThreadKeywordValue("keyword"));
+  std::unique_ptr<CrossThreadStyleValue> unit_value(
+      new CrossThreadUnitValue(1, CSSPrimitiveValue::UnitType::kDegrees));
+  std::unique_ptr<CrossThreadStyleValue> unsupported_value(
+      new CrossThreadUnsupportedValue("unsupported"));
+
+  EXPECT_FALSE(DataEquivalent(keyword_value, unit_value));
+  EXPECT_FALSE(DataEquivalent(keyword_value, unsupported_value));
+  EXPECT_FALSE(DataEquivalent(unit_value, unsupported_value));
+  EXPECT_FALSE(DataEquivalent(unit_value, keyword_value));
+  EXPECT_FALSE(DataEquivalent(unsupported_value, keyword_value));
+  EXPECT_FALSE(DataEquivalent(unsupported_value, unit_value));
+}
+
+TEST_F(CrossThreadStyleValueTest, ComparingCrossThreadKeywordValue) {
+  // CrossThreadKeywordValues are compared on their keyword; if it is equal then
+  // so are they.
+  std::unique_ptr<CrossThreadStyleValue> keyword_value_1(
+      new CrossThreadKeywordValue("keyword"));
+  std::unique_ptr<CrossThreadStyleValue> keyword_value_2(
+      new CrossThreadKeywordValue("keyword"));
+  std::unique_ptr<CrossThreadStyleValue> keyword_value_3(
+      new CrossThreadKeywordValue("different"));
+
+  EXPECT_TRUE(DataEquivalent(keyword_value_1, keyword_value_2));
+  EXPECT_FALSE(DataEquivalent(keyword_value_1, keyword_value_3));
+}
+
+TEST_F(CrossThreadStyleValueTest, ComparingCrossThreadUnitValue) {
+  // CrossThreadUnitValues are compared based on their value and unit type; both
+  // have to match. There are a lot of unit types; we just test a single sample.
+  std::unique_ptr<CrossThreadStyleValue> unit_value_1(
+      new CrossThreadUnitValue(1, CSSPrimitiveValue::UnitType::kDegrees));
+
+  // Same value, same unit.
+  std::unique_ptr<CrossThreadStyleValue> unit_value_2(
+      new CrossThreadUnitValue(1, CSSPrimitiveValue::UnitType::kDegrees));
+  EXPECT_TRUE(DataEquivalent(unit_value_1, unit_value_2));
+
+  // Same value, different unit.
+  std::unique_ptr<CrossThreadStyleValue> unit_value_3(
+      new CrossThreadUnitValue(1, CSSPrimitiveValue::UnitType::kPoints));
+  EXPECT_FALSE(DataEquivalent(unit_value_1, unit_value_3));
+
+  // Different value, same unit.
+  std::unique_ptr<CrossThreadStyleValue> unit_value_4(
+      new CrossThreadUnitValue(2, CSSPrimitiveValue::UnitType::kDegrees));
+  EXPECT_FALSE(DataEquivalent(unit_value_1, unit_value_4));
+}
+
+TEST_F(CrossThreadStyleValueTest, ComparingCrossThreadUnsupportedValue) {
+  // CrossThreadUnsupportedValues are compared on their value; if it is equal
+  // then so are they.
+  std::unique_ptr<CrossThreadStyleValue> unsupported_value_1(
+      new CrossThreadUnsupportedValue("value"));
+  std::unique_ptr<CrossThreadStyleValue> unsupported_value_2(
+      new CrossThreadUnsupportedValue("value"));
+  std::unique_ptr<CrossThreadStyleValue> unsupported_value_3(
+      new CrossThreadUnsupportedValue("different"));
+
+  EXPECT_TRUE(DataEquivalent(unsupported_value_1, unsupported_value_2));
+  EXPECT_FALSE(DataEquivalent(unsupported_value_1, unsupported_value_3));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.cc
index fa4af69f..227161d7 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.cc
@@ -12,4 +12,11 @@
   return CSSUnitValue::Create(value_, unit_);
 }
 
+bool CrossThreadUnitValue::operator==(
+    const CrossThreadStyleValue& other) const {
+  if (auto* o = DynamicTo<CrossThreadUnitValue>(other))
+    return value_ == o->value_ && unit_ == o->unit_;
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h b/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h
index d9ff1ae..0bfb8c49 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_unit_value.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
@@ -20,8 +21,11 @@
       : value_(value), unit_(unit) {}
   ~CrossThreadUnitValue() override = default;
 
+  StyleValueType GetType() const override { return StyleValueType::kUnitType; }
   CSSStyleValue* ToCSSStyleValue() override;
 
+  bool operator==(const CrossThreadStyleValue&) const override;
+
  private:
   friend class CrossThreadStyleValueTest;
 
@@ -30,6 +34,14 @@
   DISALLOW_COPY_AND_ASSIGN(CrossThreadUnitValue);
 };
 
+template <>
+struct DowncastTraits<CrossThreadUnitValue> {
+  static bool AllowFrom(const CrossThreadStyleValue& unit_value) {
+    return unit_value.GetType() ==
+           CrossThreadStyleValue::StyleValueType::kUnitType;
+  }
+};
+
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.cc
index 020bafb1..bf8ee3d 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.cc
@@ -12,4 +12,11 @@
   return CSSUnsupportedStyleValue::Create(value_);
 }
 
+bool CrossThreadUnsupportedValue::operator==(
+    const CrossThreadStyleValue& other) const {
+  if (auto* o = DynamicTo<CrossThreadUnsupportedValue>(other))
+    return value_ == o->value_;
+  return false;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.h b/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.h
index 20e085a..db24ac6 100644
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.h
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_unsupported_value.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -20,8 +21,13 @@
   explicit CrossThreadUnsupportedValue(const String& value) : value_(value) {}
   ~CrossThreadUnsupportedValue() override = default;
 
+  StyleValueType GetType() const override {
+    return StyleValueType::kUnknownType;
+  }
   CSSStyleValue* ToCSSStyleValue() override;
 
+  bool operator==(const CrossThreadStyleValue&) const override;
+
  private:
   friend class CrossThreadStyleValueTest;
 
@@ -29,6 +35,14 @@
   DISALLOW_COPY_AND_ASSIGN(CrossThreadUnsupportedValue);
 };
 
+template <>
+struct DowncastTraits<CrossThreadUnsupportedValue> {
+  static bool AllowFrom(const CrossThreadStyleValue& unsupported_value) {
+    return unsupported_value.GetType() ==
+           CrossThreadStyleValue::StyleValueType::kUnknownType;
+  }
+};
+
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index fbe64fc..ff93779 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -124,12 +124,12 @@
   if (font->GetStatus() == ResourceStatus::kDecodeError) {
     font_selector_->GetExecutionContext()->AddConsoleMessage(
         ConsoleMessage::Create(
-            kOtherMessageSource, kWarningMessageLevel,
+            kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Failed to decode downloaded font: " + font->Url().ElidedString()));
     if (font->OtsParsingMessage().length() > 1) {
       font_selector_->GetExecutionContext()->AddConsoleMessage(
           ConsoleMessage::Create(
-              kOtherMessageSource, kWarningMessageLevel,
+              kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
               "OTS parsing error: " + font->OtsParsingMessage()));
     }
   }
@@ -279,7 +279,7 @@
     if (font->IsLowPriorityLoadingAllowedForRemoteFont()) {
       font_selector_->GetExecutionContext()->AddConsoleMessage(
           ConsoleMessage::Create(
-              kInterventionMessageSource, kInfoMessageLevel,
+              kInterventionMessageSource, mojom::ConsoleMessageLevel::kInfo,
               "Slow network is detected. See "
               "https://www.chromestatus.com/feature/5636954674692096 for more "
               "details. Fallback font will be used while loading: " +
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 4124e3adc..66579d86 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
 
+#include <string>
+
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_options.h"
 #include "third_party/blink/renderer/core/display_lock/strict_yielding_display_lock_budget.h"
@@ -37,6 +39,24 @@
 const char* kLockCommitted = "Lock commit was requested.";
 }  // namespace rejection_names
 
+// Helper function to convert a display locking state to a string. Used in
+// traces.
+std::string StateToString(DisplayLockContext::State state) {
+  switch (state) {
+    case DisplayLockContext::kLocked:
+      return "kLocked";
+    case DisplayLockContext::kUpdating:
+      return "kUpdating";
+    case DisplayLockContext::kCommitting:
+      return "kCommitting";
+    case DisplayLockContext::kUnlocked:
+      return "kUnlocked";
+    case DisplayLockContext::kPendingAcquire:
+      return "kPendingAcquire";
+  }
+  return "";
+}
+
 // Helper function that returns an immediately rejected promise.
 ScriptPromise GetRejectedPromise(ScriptState* script_state,
                                  const char* rejection_reason) {
@@ -120,6 +140,7 @@
 
 ScriptPromise DisplayLockContext::acquire(ScriptState* script_state,
                                           DisplayLockOptions* options) {
+  TRACE_EVENT0("blink", "DisplayLockContext::acquire()");
   double timeout_ms = (options && options->hasTimeout())
                           ? options->timeout()
                           : kDefaultLockTimeoutMs;
@@ -167,6 +188,7 @@
 }
 
 ScriptPromise DisplayLockContext::update(ScriptState* script_state) {
+  TRACE_EVENT0("blink", "DisplayLockContext::update()");
   // Reject if we're unlocked or disconnected.
   if (state_ == kUnlocked || state_ == kPendingAcquire ||
       !element_->isConnected()) {
@@ -187,6 +209,7 @@
 }
 
 ScriptPromise DisplayLockContext::commit(ScriptState* script_state) {
+  TRACE_EVENT0("blink", "DisplayLockContext::commit()");
   // Resolve if we're already unlocked.
   if (state_ == kUnlocked)
     return GetResolvedPromise(script_state);
@@ -214,6 +237,8 @@
 }
 
 ScriptPromise DisplayLockContext::updateAndCommit(ScriptState* script_state) {
+  TRACE_EVENT0("blink", "DisplayLockContext::updateAndCommit()");
+
   // Resolve if we're already unlocked.
   if (state_ == kUnlocked)
     return GetResolvedPromise(script_state);
@@ -802,10 +827,20 @@
   if (new_state == state_)
     return *this;
 
+  if (state_ == kUnlocked) {
+    TRACE_EVENT_ASYNC_BEGIN0("blink", "LockedDisplayLock", this);
+  } else if (new_state == kUnlocked) {
+    TRACE_EVENT_ASYNC_END0("blink", "LockedDisplayLock", this);
+  }
+
   bool was_activatable = context_->IsActivatable();
   bool was_locked = context_->IsLocked();
 
   state_ = new_state;
+  if (state_ != kUnlocked) {
+    TRACE_EVENT_ASYNC_STEP_INTO0("blink", "LockedDisplayLock", this,
+                                 StateToString(state_));
+  }
 
   if (!context_->document_)
     return *this;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 40077f2..e965a536 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -50,6 +50,15 @@
     kDefault = kYieldBetweenLifecyclePhases
   };
 
+  // The current state of the lock. Note that the order of these matters.
+  enum State {
+    kLocked,
+    kUpdating,
+    kCommitting,
+    kUnlocked,
+    kPendingAcquire,
+  };
+
   // See GetScopedPendingFrameRect() for description.
   class ScopedPendingFrameRect {
     STACK_ALLOCATED();
@@ -162,15 +171,6 @@
   friend class DisplayLockSuspendedHandle;
   friend class DisplayLockBudget;
 
-  // The current state of the lock. Note that the order of these matters.
-  enum State {
-    kLocked,
-    kUpdating,
-    kCommitting,
-    kUnlocked,
-    kPendingAcquire,
-  };
-
   class StateChangeHelper {
     DISALLOW_NEW();
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index e7debdfc..a60ac38 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1383,7 +1383,7 @@
         // The above removeChild() can execute arbitrary JavaScript code.
         if (source->parentNode()) {
           AddConsoleMessage(ConsoleMessage::Create(
-              kJSMessageSource, kWarningMessageLevel,
+              kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
               ExceptionMessages::FailedToExecute("adoptNode", "Document",
                                                  "Unable to remove the "
                                                  "specified node from the "
@@ -3947,7 +3947,7 @@
   if (!has_insertion_point) {
     if (ignore_destructive_write_count_) {
       AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
           ExceptionMessages::FailedToExecute(
               "write", "Document",
               "It isn't possible to write into a document "
@@ -4187,7 +4187,7 @@
         base_element_url.ProtocolIsJavaScript()) {
       UseCounter::Count(*this, WebFeature::kBaseWithDataHref);
       AddConsoleMessage(ConsoleMessage::Create(
-          kSecurityMessageSource, kErrorMessageLevel,
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
           "'" + base_element_url.Protocol() +
               "' URLs may not be used as base URLs for a document."));
     }
@@ -4304,8 +4304,8 @@
   if (refresh_url.ProtocolIsJavaScript()) {
     String message =
         "Refused to refresh " + url_.ElidedString() + " to a javascript: URL";
-    AddConsoleMessage(ConsoleMessage::Create(kSecurityMessageSource,
-                                             kErrorMessageLevel, message));
+    AddConsoleMessage(ConsoleMessage::Create(
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message));
     return;
   }
 
@@ -4315,8 +4315,8 @@
         "Refused to execute the redirect specified via '<meta "
         "http-equiv='refresh' content='...'>'. The document is sandboxed, and "
         "the 'allow-scripts' keyword is not set.";
-    AddConsoleMessage(ConsoleMessage::Create(kSecurityMessageSource,
-                                             kErrorMessageLevel, message));
+    AddConsoleMessage(ConsoleMessage::Create(
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message));
     return;
   }
   frame_->GetNavigationScheduler().ScheduleRedirect(delay, refresh_url,
@@ -6341,9 +6341,9 @@
   auto declared_policy = ParseFeaturePolicyHeader(
       feature_policy_header, GetSecurityOrigin(), &messages, this);
   for (auto& message : messages) {
-    AddConsoleMessage(
-        ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel,
-                               "Error with Feature-Policy header: " + message));
+    AddConsoleMessage(ConsoleMessage::Create(
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
+        "Error with Feature-Policy header: " + message));
   }
   if (GetSandboxFlags() != kSandboxNone &&
       RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
@@ -6409,7 +6409,7 @@
   // later. In that case, any subsequent violations will be correctly reported.
   if (!origin_trials::FeaturePolicyReportingEnabled(this)) {
     AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kWarningMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Feature-Policy-Report-Only header will have no effect unless Feature "
         "Policy reporting is enabled with an Origin Trial. Sign up at "
         "https://developers.chrome.com/origintrials/"));
@@ -6421,7 +6421,7 @@
       feature_policy_report_only_header, GetSecurityOrigin(), &messages, this);
   for (auto& message : messages) {
     AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Error with Feature-Policy-Report-Only header: " + message));
   }
 
@@ -6716,7 +6716,7 @@
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     if (reason == kAboutToExecuteScript) {
       AddConsoleMessage(ConsoleMessage::Create(
-          kSecurityMessageSource, kErrorMessageLevel,
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
           "Blocked script execution in '" + Url().ElidedString() +
               "' because the document's frame is sandboxed and the "
               "'allow-scripts' permission is not set."));
@@ -6874,7 +6874,7 @@
 }
 
 static void RunAddConsoleMessageTask(MessageSource source,
-                                     MessageLevel level,
+                                     mojom::ConsoleMessageLevel level,
                                      const String& message,
                                      ExecutionContext* context) {
   ConsoleMessage* console_message =
@@ -7901,7 +7901,7 @@
   // TODO(iclelland): Report something different in report-only mode
   if (disposition == mojom::FeaturePolicyDisposition::kEnforce) {
     frame->Console().AddMessage(ConsoleMessage::Create(
-        kViolationMessageSource, kErrorMessageLevel,
+        kViolationMessageSource, mojom::ConsoleMessageLevel::kError,
         (message.IsEmpty() ? ("Feature policy violation: " + feature_name +
                               " is not allowed in this document.")
                            : message)));
@@ -8002,7 +8002,7 @@
     report_endpoints.push_back(end_point);
 
   AddConsoleMessage(ConsoleMessage::Create(kSecurityMessageSource,
-                                           kErrorMessageLevel,
+                                           mojom::ConsoleMessageLevel::kError,
                                            violation_params->console_message));
   GetContentSecurityPolicy()->ReportViolation(
       violation_params->directive,
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index 599872c..4848d06 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -309,7 +309,8 @@
 
         executing_window->GetFrame()->Console().AddMessage(
             ConsoleMessage::Create(
-                kInterventionMessageSource, kWarningMessageLevel,
+                kInterventionMessageSource,
+                mojom::ConsoleMessageLevel::kWarning,
                 "Registering mousewheel event as passive due to "
                 "smoothscroll.js usage. The smoothscroll.js library is "
                 "buggy, no longer necessary and degrades performance. See "
diff --git a/third_party/blink/renderer/core/editing/commands/document_exec_command.cc b/third_party/blink/renderer/core/editing/commands/document_exec_command.cc
index fcae634b..64cfbf6 100644
--- a/third_party/blink/renderer/core/editing/commands/document_exec_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/document_exec_command.cc
@@ -80,8 +80,8 @@
     String message =
         "We don't execute document.execCommand() this time, because it is "
         "called recursively.";
-    AddConsoleMessage(ConsoleMessage::Create(kJSMessageSource,
-                                             kWarningMessageLevel, message));
+    AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     return false;
   }
   base::AutoReset<bool> execute_scope(&is_running_exec_command_, true);
diff --git a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
index d866658d..899b1c8 100644
--- a/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
+++ b/third_party/blink/renderer/core/editing/commands/editing_commands_utilities.cc
@@ -532,7 +532,7 @@
   // non-<html> root elements under <body>, and the <body> works as
   // rootEditableElement.
   document.AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kWarningMessageLevel,
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
       "document.execCommand() doesn't work with an invalid HTML structure. It "
       "is corrected automatically."));
   UseCounter::Count(document, WebFeature::kExecCommandAltersHTMLStructure);
diff --git a/third_party/blink/renderer/core/editing/dom_selection.cc b/third_party/blink/renderer/core/editing/dom_selection.cc
index 45ed2e1..4a3011a 100644
--- a/third_party/blink/renderer/core/editing/dom_selection.cc
+++ b/third_party/blink/renderer/core/editing/dom_selection.cc
@@ -842,7 +842,7 @@
 void DOMSelection::AddConsoleWarning(const String& message) {
   if (tree_scope_) {
     tree_scope_->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
   }
 }
 
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 541be41..06c8e61 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -737,22 +737,6 @@
 
   ChildListMutationScope mutation(*container_node);
 
-  // FIXME: This is wrong if containerNode->firstChild() has more than one ref!
-  // Example:
-  // <div>foo</div>
-  // <script>
-  // var oldText = div.firstChild;
-  // console.log(oldText.data); // foo
-  // div.innerText = "bar";
-  // console.log(oldText.data); // bar!?!
-  // </script>
-  // I believe this is an intentional benchmark cheat from years ago.
-  // We should re-visit if we actually want this still.
-  if (container_node->HasOneTextChild()) {
-    ToText(container_node->firstChild())->setData(text);
-    return;
-  }
-
   // NOTE: This method currently always creates a text node, even if that text
   // node will be empty.
   Text* text_node = Text::Create(container_node->GetDocument(), text);
diff --git a/third_party/blink/renderer/core/events/touch_event_test.cc b/third_party/blink/renderer/core/events/touch_event_test.cc
index b987a51..cab98c7 100644
--- a/third_party/blink/renderer/core/events/touch_event_test.cc
+++ b/third_party/blink/renderer/core/events/touch_event_test.cc
@@ -24,7 +24,7 @@
   // ChromeClient methods:
   void AddMessageToConsole(LocalFrame*,
                            MessageSource message_source,
-                           MessageLevel,
+                           mojom::ConsoleMessageLevel,
                            const String& message,
                            unsigned line_number,
                            const String& source_id,
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc
index 4cc9282..5af50879 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.cc
+++ b/third_party/blink/renderer/core/execution_context/execution_context.cc
@@ -211,7 +211,7 @@
                                   : kDoNotSupportReferrerPolicyLegacyKeywords,
           &referrer_policy)) {
     AddConsoleMessage(ConsoleMessage::Create(
-        kRenderingMessageSource, kErrorMessageLevel,
+        kRenderingMessageSource, mojom::ConsoleMessageLevel::kError,
         "Failed to set referrer policy: The value '" + policies +
             "' is not one of " +
             (support_legacy_keywords
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 f5fc44b..85d653bd 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -10547,7 +10547,7 @@
   web_view_impl->MainFrameImpl()->CollectGarbageForTesting();
 }
 
-TEST_F(WebFrameTest, ImageDocumentLoadFinishTime) {
+TEST_F(WebFrameTest, ImageDocumentLoadResponseEnd) {
   // Loading an image resource directly generates an ImageDocument with
   // the document loader feeding image data into the resource of a generated
   // img tag. We expect the load finish time to be the same for the document
@@ -10566,12 +10566,12 @@
   ImageResource* resource = img_document->CachedImageResourceDeprecated();
 
   EXPECT_TRUE(resource);
-  EXPECT_NE(TimeTicks(), resource->LoadFinishTime());
+  EXPECT_NE(TimeTicks(), resource->LoadResponseEnd());
 
   DocumentLoader* loader = document->Loader();
 
   EXPECT_TRUE(loader);
-  EXPECT_EQ(loader->GetTiming().ResponseEnd(), resource->LoadFinishTime());
+  EXPECT_EQ(loader->GetTiming().ResponseEnd(), resource->LoadResponseEnd());
 }
 
 TEST_F(WebFrameTest, CopyImageDocument) {
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index a324ea3..97b1979 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -116,7 +116,7 @@
 
   void AddMessageToConsole(LocalFrame*,
                            MessageSource,
-                           MessageLevel,
+                           mojom::ConsoleMessageLevel,
                            const String& message,
                            unsigned line_number,
                            const String&,
diff --git a/third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc b/third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc
index 60e36eb..b8da5b0 100644
--- a/third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc
+++ b/third_party/blink/renderer/core/feature_policy/dom_feature_policy.cc
@@ -31,7 +31,7 @@
       SecurityOrigin::CreateFromString(url);
   if (!origin || origin->IsOpaque()) {
     GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Invalid origin url for feature '" + feature + "': " + url + "."));
     return false;
   }
@@ -98,9 +98,9 @@
 
 void DOMFeaturePolicy::AddWarningForUnrecognizedFeature(
     const String& feature) const {
-  GetDocument()->AddConsoleMessage(
-      ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
-                             "Unrecognized feature: '" + feature + "'."));
+  GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
+      kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      "Unrecognized feature: '" + feature + "'."));
 }
 
 void DOMFeaturePolicy::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 30897ef..8b1e9d5 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -818,8 +818,8 @@
   if (execution_context_->IsContextDestroyed())
     return;
   if (!message.IsEmpty()) {
-    execution_context_->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel, message));
+    execution_context_->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError, message));
   }
   if (resolver_) {
     ScriptState* state = resolver_->GetScriptState();
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index 0399b9ea..e4112a6 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -1198,7 +1198,7 @@
 
   String message =
       "Unrecognized Content-Security-Policy directive '" + name + "'.\n";
-  MessageLevel level = kErrorMessageLevel;
+  mojom::ConsoleMessageLevel level = mojom::ConsoleMessageLevel::kError;
   if (EqualIgnoringASCIICase(name, kAllow)) {
     message = kAllowMessage;
   } else if (EqualIgnoringASCIICase(name, kOptions)) {
@@ -1208,7 +1208,7 @@
   } else if (GetDirectiveType(name) != DirectiveType::kUndefined) {
     message = "The Content-Security-Policy directive '" + name +
               "' is implemented behind a flag which is currently disabled.\n";
-    level = kInfoMessageLevel;
+    level = mojom::ConsoleMessageLevel::kInfo;
   }
 
   LogToConsole(message, level);
@@ -1317,7 +1317,7 @@
 }
 
 void ContentSecurityPolicy::LogToConsole(const String& message,
-                                         MessageLevel level) {
+                                         mojom::ConsoleMessageLevel level) {
   LogToConsole(ConsoleMessage::Create(kSecurityMessageSource, level, message));
 }
 
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
index 0a1f91d..3866754d 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -29,13 +29,13 @@
 #include <memory>
 #include <utility>
 
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/public/platform/web_content_security_policy_struct.h"
 #include "third_party/blink/public/platform/web_insecure_request_policy.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
-#include "third_party/blink/renderer/core/inspector/console_types.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
@@ -507,7 +507,9 @@
 
   void ApplyPolicySideEffectsToDelegate();
 
-  void LogToConsole(const String& message, MessageLevel = kErrorMessageLevel);
+  void LogToConsole(
+      const String& message,
+      mojom::ConsoleMessageLevel = mojom::ConsoleMessageLevel::kError);
 
   void AddAndReportPolicyFromHeaderValue(const String&,
                                          ContentSecurityPolicyHeaderType,
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index 22c42b5..e0d298e 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -172,8 +172,8 @@
     ContentSecurityPolicy::ViolationType violation_type) const {
   String message =
       IsReportOnly() ? "[Report Only] " + console_message : console_message;
-  policy_->LogToConsole(ConsoleMessage::Create(kSecurityMessageSource,
-                                               kErrorMessageLevel, message));
+  policy_->LogToConsole(ConsoleMessage::Create(
+      kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message));
   policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                            report_endpoints_, use_reporting_api_, header_,
                            header_type_, violation_type,
@@ -190,9 +190,10 @@
     LocalFrame* frame) const {
   String message =
       IsReportOnly() ? "[Report Only] " + console_message : console_message;
-  policy_->LogToConsole(ConsoleMessage::Create(kSecurityMessageSource,
-                                               kErrorMessageLevel, message),
-                        frame);
+  policy_->LogToConsole(
+      ConsoleMessage::Create(kSecurityMessageSource,
+                             mojom::ConsoleMessageLevel::kError, message),
+      frame);
   policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                            report_endpoints_, use_reporting_api_, header_,
                            header_type_, ContentSecurityPolicy::kURLViolation,
@@ -212,9 +213,9 @@
       IsReportOnly() ? "[Report Only] " + console_message : console_message;
   std::unique_ptr<SourceLocation> source_location =
       SourceLocation::Capture(context_url, context_line.OneBasedInt(), 0);
-  policy_->LogToConsole(ConsoleMessage::Create(kSecurityMessageSource,
-                                               kErrorMessageLevel, message,
-                                               source_location->Clone()));
+  policy_->LogToConsole(ConsoleMessage::Create(
+      kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message,
+      source_location->Clone()));
   policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
                            report_endpoints_, use_reporting_api_, header_,
                            header_type_,
@@ -239,7 +240,8 @@
   if (IsReportOnly() ||
       exception_status == ContentSecurityPolicy::kWillNotThrowException) {
     ConsoleMessage* console_message = ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel, report_message);
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
+        report_message);
     policy_->LogToConsole(console_message);
   }
   policy_->ReportViolation(directive_text, effective_type, message, blocked_url,
diff --git a/third_party/blink/renderer/core/frame/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation.cc
index 1e34763..ec51f32 100644
--- a/third_party/blink/renderer/core/frame/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation.cc
@@ -711,8 +711,9 @@
   String message = DeprecationMessage(unresolved_property);
   if (!message.IsEmpty()) {
     page->GetDeprecation().Suppress(unresolved_property);
-    ConsoleMessage* console_message = ConsoleMessage::Create(
-        kDeprecationMessageSource, kWarningMessageLevel, message);
+    ConsoleMessage* console_message =
+        ConsoleMessage::Create(kDeprecationMessageSource,
+                               mojom::ConsoleMessageLevel::kWarning, message);
     frame->Console().AddMessage(console_message);
   }
 }
@@ -795,7 +796,8 @@
   // Send the deprecation message to the console as a warning.
   DCHECK(!info.message.IsEmpty());
   ConsoleMessage* console_message = ConsoleMessage::Create(
-      kDeprecationMessageSource, kWarningMessageLevel, info.message);
+      kDeprecationMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      info.message);
   frame->Console().AddMessage(console_message);
 
   if (!frame || !frame->Client())
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index e59311c..a911157 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -333,7 +333,7 @@
       !allow_scripts_to_close_windows) {
     active_document->domWindow()->GetFrameConsole()->AddMessage(
         ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Scripts may close only the windows that were opened by it."));
     return;
   }
diff --git a/third_party/blink/renderer/core/frame/frame_console.cc b/third_party/blink/renderer/core/frame/frame_console.cc
index 607e38de..5e8cf02 100644
--- a/third_party/blink/renderer/core/frame/frame_console.cc
+++ b/third_party/blink/renderer/core/frame/frame_console.cc
@@ -61,7 +61,7 @@
 }
 
 void FrameConsole::ReportMessageToClient(MessageSource source,
-                                         MessageLevel level,
+                                         mojom::ConsoleMessageLevel level,
                                          const String& message,
                                          SourceLocation* location) {
   if (source == kNetworkMessageSource)
@@ -105,7 +105,7 @@
       String::Number(response.HttpStatusCode()) + " (" +
       response.HttpStatusText() + ')';
   ConsoleMessage* console_message = ConsoleMessage::CreateForRequest(
-      kNetworkMessageSource, kErrorMessageLevel, message,
+      kNetworkMessageSource, mojom::ConsoleMessageLevel::kError, message,
       response.CurrentRequestUrl().GetString(), loader, request_identifier);
   AddMessage(console_message);
 }
@@ -122,8 +122,8 @@
     message.Append(error.LocalizedDescription());
   }
   AddMessageToStorage(ConsoleMessage::CreateForRequest(
-      kNetworkMessageSource, kErrorMessageLevel, message.ToString(),
-      error.FailingURL(), loader, request_identifier));
+      kNetworkMessageSource, mojom::ConsoleMessageLevel::kError,
+      message.ToString(), error.FailingURL(), loader, request_identifier));
 }
 
 void FrameConsole::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/frame/frame_console.h b/third_party/blink/renderer/core/frame/frame_console.h
index 71722a5..192da82 100644
--- a/third_party/blink/renderer/core/frame/frame_console.h
+++ b/third_party/blink/renderer/core/frame/frame_console.h
@@ -29,8 +29,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_CONSOLE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_CONSOLE_H_
 
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/inspector/console_types.h"
 #include "third_party/blink/renderer/core/loader/console_logger_impl_base.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -69,7 +69,7 @@
 
   bool AddMessageToStorage(ConsoleMessage*);
   void ReportMessageToClient(MessageSource,
-                             MessageLevel,
+                             mojom::ConsoleMessageLevel,
                              const String& message,
                              SourceLocation*);
 
diff --git a/third_party/blink/renderer/core/frame/intervention.cc b/third_party/blink/renderer/core/frame/intervention.cc
index 10b9e7ee..f3f654e 100644
--- a/third_party/blink/renderer/core/frame/intervention.cc
+++ b/third_party/blink/renderer/core/frame/intervention.cc
@@ -27,7 +27,7 @@
   // Send the message to the console.
   Document* document = frame->GetDocument();
   document->AddConsoleMessage(ConsoleMessage::Create(
-      kInterventionMessageSource, kErrorMessageLevel, message));
+      kInterventionMessageSource, mojom::ConsoleMessageLevel::kError, message));
 
   if (!frame->Client())
     return;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 275ea60..ede46e4 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -587,9 +587,9 @@
           "The target origin provided ('" + intended_target_origin->ToString() +
               "') does not match the recipient window's origin ('" +
               document()->GetSecurityOrigin()->ToString() + "').");
-      ConsoleMessage* console_message =
-          ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel,
-                                 message, std::move(location));
+      ConsoleMessage* console_message = ConsoleMessage::Create(
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message,
+          std::move(location));
       GetFrameConsole()->AddMessage(console_message);
       return;
     }
@@ -660,7 +660,7 @@
   if (document()->IsSandboxed(kSandboxModals)) {
     UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Ignored call to 'alert()'. The document is sandboxed, and the "
         "'allow-modals' keyword is not set."));
     return;
@@ -689,7 +689,7 @@
   if (document()->IsSandboxed(kSandboxModals)) {
     UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Ignored call to 'confirm()'. The document is sandboxed, and the "
         "'allow-modals' keyword is not set."));
     return false;
@@ -720,7 +720,7 @@
   if (document()->IsSandboxed(kSandboxModals)) {
     UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Ignored call to 'prompt()'. The document is sandboxed, and the "
         "'allow-modals' keyword is not set."));
     return String();
@@ -1316,7 +1316,7 @@
         "event. Please make sure it has an appropriate `as` value and it is " +
         "preloaded intentionally.";
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
   }
 }
 
@@ -1405,8 +1405,8 @@
   if (message.IsEmpty())
     return;
 
-  GetFrameConsole()->AddMessage(
-      ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel, message));
+  GetFrameConsole()->AddMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kError, message));
 }
 
 DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate,
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index d10a4d9..487d19c 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -481,8 +481,8 @@
 }
 
 void LocalFrame::PrintNavigationWarning(const String& message) {
-  console_->AddMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, message));
+  console_->AddMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
 }
 
 bool LocalFrame::ShouldClose() {
diff --git a/third_party/blink/renderer/core/frame/navigation_rate_limiter.cc b/third_party/blink/renderer/core/frame/navigation_rate_limiter.cc
index 0a2757e6..84b2de1 100644
--- a/third_party/blink/renderer/core/frame/navigation_rate_limiter.cc
+++ b/third_party/blink/renderer/core/frame/navigation_rate_limiter.cc
@@ -49,7 +49,7 @@
     error_message_sent_ = true;
     if (auto* local_frame = DynamicTo<LocalFrame>(frame_.Get())) {
       local_frame->Console().AddMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "Throttling navigation to prevent the browser from hanging. See "
           "https://crbug.com/882238. Command line switch "
           "--disable-ipc-flooding-protection can be used to bypass the "
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index d948617..b6e0e2c9 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -63,7 +63,7 @@
                                      UserGestureStatus user_gesture_status) {
   if (!origin_document.GetSecurityOrigin()->CanDisplay(url)) {
     origin_document.AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Not allowed to load local resource: " + url.ElidedString()));
     return;
   }
diff --git a/third_party/blink/renderer/core/frame/scheduling.cc b/third_party/blink/renderer/core/frame/scheduling.cc
index a05ab63..bd915e17 100644
--- a/third_party/blink/renderer/core/frame/scheduling.cc
+++ b/third_party/blink/renderer/core/frame/scheduling.cc
@@ -26,7 +26,7 @@
     // a process are part of the same site to avoid leaking cross-site inputs.
     ExecutionContext::From(script_state)
         ->AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "isInputPending requires site-per-process (crbug.com/910421)."));
     return false;
   }
@@ -50,7 +50,8 @@
       message.Append("\". Skipping.");
       ExecutionContext::From(script_state)
           ->AddConsoleMessage(ConsoleMessage::Create(
-              kJSMessageSource, kWarningMessageLevel, message.ToString()));
+              kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+              message.ToString()));
     }
 
     if (!has_pending_input)
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 17c5cf4..567bd87e 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -239,8 +239,8 @@
   String message = ExceptionMessages::FailedToExecute(
       "requestFullscreen", "Element",
       "API can only be initiated by a user gesture.");
-  document.AddConsoleMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, message));
+  document.AddConsoleMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
 
   return false;
 }
diff --git a/third_party/blink/renderer/core/html/forms/base_text_input_type.cc b/third_party/blink/renderer/core/html/forms/base_text_input_type.cc
index e4189959..fcde627 100644
--- a/third_party/blink/renderer/core/html/forms/base_text_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/base_text_input_type.cc
@@ -88,11 +88,11 @@
         new ScriptRegexp(raw_pattern, kTextCaseSensitive, kMultilineDisabled,
                          ScriptRegexp::UTF16));
     if (!raw_regexp->IsValid()) {
-      GetElement().GetDocument().AddConsoleMessage(
-          ConsoleMessage::Create(kRenderingMessageSource, kErrorMessageLevel,
-                                 "Pattern attribute value " + raw_pattern +
-                                     " is not a valid regular expression: " +
-                                     raw_regexp->ExceptionMessage()));
+      GetElement().GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+          kRenderingMessageSource, mojom::ConsoleMessageLevel::kError,
+          "Pattern attribute value " + raw_pattern +
+              " is not a valid regular expression: " +
+              raw_regexp->ExceptionMessage()));
       regexp_.reset(raw_regexp.release());
       pattern_for_regexp_ = raw_pattern;
       return false;
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index 3cfb161..47054c0 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -165,7 +165,7 @@
     String message =
         "File chooser dialog can only be shown with a user activation.";
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     return;
   }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 3a77be8..90b16f9 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -220,7 +220,7 @@
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     element->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Blocked autofocusing on a form control because the form's frame is "
         "sandboxed and the 'allow-scripts' permission is not set."));
     return false;
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index f1443608..913a642 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -251,8 +251,9 @@
       String message(
           "An invalid form control with name='%name' is not focusable.");
       message.Replace("%name", unhandled->GetName());
-      GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kRenderingMessageSource, kErrorMessageLevel, message));
+      GetDocument().AddConsoleMessage(
+          ConsoleMessage::Create(kRenderingMessageSource,
+                                 mojom::ConsoleMessageLevel::kError, message));
     }
   }
   return false;
@@ -267,14 +268,14 @@
 
   if (!isConnected()) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Form submission canceled because the form is not connected"));
     return;
   }
 
   if (GetDocument().IsSandboxed(kSandboxForms)) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Blocked form submission to '" + attributes_.Action() +
             "' because the form's frame is sandboxed and the 'allow-forms' "
             "permission is not set."));
@@ -290,7 +291,7 @@
       if (RuntimeEnabledFeatures::UnclosedFormControlIsInvalidEnabled()) {
         String tag_name = ToHTMLFormControlElement(element)->tagName();
         GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-            kSecurityMessageSource, kErrorMessageLevel,
+            kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
             "Form submission failed, as the <" + tag_name +
                 "> element named "
                 "'" +
@@ -360,17 +361,17 @@
   // context flag set, then abort these steps without doing anything.
   if (!isConnected()) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Form submission canceled because the form is not connected"));
     return;
   }
 
   if (is_constructing_entry_list_) {
     DCHECK(RuntimeEnabledFeatures::FormDataEventEnabled());
-    GetDocument().AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                               "Form submission canceled because the form is "
-                               "constructing entry list"));
+    GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "Form submission canceled because the form is "
+        "constructing entry list"));
     return;
   }
 
@@ -404,7 +405,7 @@
   // 'formdata' event handlers might disconnect the form.
   if (RuntimeEnabledFeatures::FormDataEventEnabled() && !isConnected()) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Form submission canceled because the form is not connected"));
     return;
   }
@@ -461,7 +462,7 @@
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Blocked form submission to '" + submission->Action().ElidedString() +
             "' because the form's frame is sandboxed and the 'allow-forms' "
             "permission is not set."));
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index d6e5e1f..53de70b 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -380,7 +380,7 @@
   if (index >= kMaxListItems ||
       GetListItems().size() + diff + 1 > kMaxListItems) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         String::Format("Blocked to expand the option list and set an option at "
                        "index=%u.  The maximum list length is %u.",
                        index, kMaxListItems)));
@@ -412,7 +412,7 @@
   if (new_len > kMaxListItems ||
       GetListItems().size() + new_len - length() > kMaxListItems) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         String::Format("Blocked to expand the option list to %u items.  The "
                        "maximum list length is %u.",
                        new_len, kMaxListItems)));
diff --git a/third_party/blink/renderer/core/html/forms/input_type.cc b/third_party/blink/renderer/core/html/forms/input_type.cc
index a60dbe4f..33c440ab 100644
--- a/third_party/blink/renderer/core/html/forms/input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/input_type.cc
@@ -909,7 +909,7 @@
 void InputType::AddWarningToConsole(const char* message_format,
                                     const String& value) const {
   GetElement().GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-      kRenderingMessageSource, kWarningMessageLevel,
+      kRenderingMessageSource, mojom::ConsoleMessageLevel::kWarning,
       String::Format(message_format,
                      JSONValue::QuoteString(value).Utf8().data())));
 }
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.cc b/third_party/blink/renderer/core/html/forms/listed_element.cc
index 1ba4f3e..0de55bc 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.cc
+++ b/third_party/blink/renderer/core/html/forms/listed_element.cc
@@ -490,7 +490,7 @@
         "An invalid form control with name='%name' is not focusable.");
     message.Replace("%name", GetName());
     element.GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kRenderingMessageSource, kErrorMessageLevel, message));
+        kRenderingMessageSource, mojom::ConsoleMessageLevel::kError, message));
   }
   return false;
 }
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index e19dcef..8d2b3ff8 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -148,7 +148,7 @@
                                                         invalid_tokens));
     if (!invalid_tokens.IsNull()) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kErrorMessageLevel,
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
           "Error while parsing the 'sandbox' attribute: " + invalid_tokens));
     }
     if (RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
@@ -157,7 +157,8 @@
       if (!messages.IsEmpty()) {
         for (const String& message : messages) {
           GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-              kOtherMessageSource, kWarningMessageLevel, message));
+              kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+              message));
         }
       }
     }
@@ -197,7 +198,7 @@
             value.GetString(), GetDocument().RequiredCSP().GetString())) {
       required_csp_ = g_null_atom;
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kErrorMessageLevel,
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
           "'csp' attribute is not a valid policy: " + value));
       return;
     }
@@ -213,7 +214,8 @@
       if (!messages.IsEmpty()) {
         for (const String& message : messages) {
           GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-              kOtherMessageSource, kWarningMessageLevel, message));
+              kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+              message));
         }
       }
       if (!value.IsEmpty()) {
@@ -233,11 +235,11 @@
             WebFeature::kHTMLIFrameElementGestureMedia)) {
       UseCounter::Count(GetDocument(),
                         WebFeature::kHTMLIFrameElementGestureMedia);
-      GetDocument().AddConsoleMessage(
-          ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
-                                 "<iframe gesture=\"media\"> is not supported. "
-                                 "Use <iframe allow=\"autoplay\">, "
-                                 "https://goo.gl/ximf56"));
+      GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+          "<iframe gesture=\"media\"> is not supported. "
+          "Use <iframe allow=\"autoplay\">, "
+          "https://goo.gl/ximf56"));
     }
 
     if (name == kSrcAttr)
@@ -329,7 +331,7 @@
             required_csp_, GetDocument().RequiredCSP().GetString())) {
       if (!required_csp_.IsEmpty()) {
         GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-            kOtherMessageSource, kErrorMessageLevel,
+            kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
             "'csp' attribute is not a valid policy: " + required_csp_));
       }
       if (required_csp_ != GetDocument().RequiredCSP()) {
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index 48ce62b..4f21bde8 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -311,7 +311,7 @@
             &is_default_overridden_intrinsic_size_, &message);
     if (!message.IsEmpty()) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kWarningMessageLevel, message));
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     }
 
     if (intrinsic_size_changed && GetLayoutObject() &&
diff --git a/third_party/blink/renderer/core/html/html_link_element.cc b/third_party/blink/renderer/core/html/html_link_element.cc
index 64a04a6..5ddd770 100644
--- a/third_party/blink/renderer/core/html/html_link_element.cc
+++ b/third_party/blink/renderer/core/html/html_link_element.cc
@@ -245,7 +245,7 @@
   if (!ShouldLoadLink() && IsInShadowTree()) {
     String message = "HTML element <link> is ignored in shadow tree.";
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     return kInsertionDone;
   }
 
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index 1d29a4b..c5b25dc 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -129,8 +129,9 @@
     String message =
         "Error parsing a meta element's content: ';' is not a valid key-value "
         "pair separator. Please use ',' instead.";
-    document->AddConsoleMessage(ConsoleMessage::Create(
-        kRenderingMessageSource, kWarningMessageLevel, message));
+    document->AddConsoleMessage(
+        ConsoleMessage::Create(kRenderingMessageSource,
+                               mojom::ConsoleMessageLevel::kWarning, message));
   }
 }
 
@@ -402,7 +403,8 @@
   return kErrors[error_code];
 }
 
-static MessageLevel ViewportErrorMessageLevel(ViewportErrorCode error_code) {
+static mojom::ConsoleMessageLevel ViewportErrorMessageLevel(
+    ViewportErrorCode error_code) {
   switch (error_code) {
     case kTruncatedViewportArgumentValueError:
     case kTargetDensityDpiUnsupported:
@@ -410,11 +412,11 @@
     case kUnrecognizedViewportArgumentValueError:
     case kMaximumScaleTooLargeError:
     case kViewportFitUnsupported:
-      return kWarningMessageLevel;
+      return mojom::ConsoleMessageLevel::kWarning;
   }
 
   NOTREACHED();
-  return kErrorMessageLevel;
+  return mojom::ConsoleMessageLevel::kError;
 }
 
 void HTMLMetaElement::ReportViewportWarning(Document* document,
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index b0d373d..e9312233 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -696,12 +696,12 @@
 bool HTMLPlugInElement::AllowedToLoadPlugin(const KURL& url,
                                             const String& mime_type) {
   if (GetDocument().IsSandboxed(kSandboxPlugins)) {
-    GetDocument().AddConsoleMessage(
-        ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel,
-                               "Failed to load '" + url.ElidedString() +
-                                   "' as a plugin, because the "
-                                   "frame into which the plugin "
-                                   "is loading is sandboxed."));
+    GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
+        "Failed to load '" + url.ElidedString() +
+            "' as a plugin, because the "
+            "frame into which the plugin "
+            "is loading is sandboxed."));
     return false;
   }
   return true;
diff --git a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
index f1cfd96..f07caec 100644
--- a/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_image_observer.cc
@@ -99,7 +99,7 @@
 
   if (!lazy_load_intersection_observer_) {
     root_document->AddConsoleMessage(ConsoleMessage::Create(
-        kInterventionMessageSource, kInfoMessageLevel,
+        kInterventionMessageSource, mojom::ConsoleMessageLevel::kInfo,
         "Images loaded lazily and replaced with placeholders. Load events are "
         "deferred. See https://crbug.com/846170"));
     lazy_load_intersection_observer_ = IntersectionObserver::Create(
diff --git a/third_party/blink/renderer/core/html/media/autoplay_policy.cc b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
index af5cb42..38713aa 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_policy.cc
+++ b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
@@ -224,7 +224,8 @@
     if (IsGestureNeededForPlayback()) {
       if (IsUsingDocumentUserActivationRequiredPolicy()) {
         element_->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel, kWarningUnmuteFailed));
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+            kWarningUnmuteFailed));
       }
 
       autoplay_uma_helper_->RecordAutoplayUnmuteStatus(
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 4896f2e6..6d78810 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -1463,7 +1463,7 @@
   if (!frame || !GetDocument().GetSecurityOrigin()->CanDisplay(url)) {
     if (action_if_invalid == kComplain) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kSecurityMessageSource, kErrorMessageLevel,
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
           "Not allowed to load local resource: " + url.ElidedString()));
     }
     DVLOG(3) << "isSafeToLoadURL(" << (void*)this << ", "
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index d25274e..c39bb4ce 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -232,7 +232,7 @@
             &is_default_overridden_intrinsic_size_, &message);
     if (!message.IsEmpty()) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kWarningMessageLevel, message));
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     }
 
     if (intrinsic_size_changed && GetLayoutObject() &&
diff --git a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
index 637d43e..8a754cba 100644
--- a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
@@ -197,7 +197,8 @@
     error_message.Append("Failed parsing 'srcset' attribute value since ");
     error_message.Append(message);
     document->GetFrame()->Console().AddMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kErrorMessageLevel, error_message.ToString()));
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
+        error_message.ToString()));
   }
 }
 
@@ -327,7 +328,7 @@
           UseCounter::Count(document, WebFeature::kSrcsetDroppedCandidate);
           if (document->GetFrame()) {
             document->GetFrame()->Console().AddMessage(ConsoleMessage::Create(
-                kOtherMessageSource, kErrorMessageLevel,
+                kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
                 String("Dropped srcset candidate ") +
                     JSONValue::QuoteString(
                         String(image_url_start,
diff --git a/third_party/blink/renderer/core/html/parser/xss_auditor.cc b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
index 1f53134..48aa1e2 100644
--- a/third_party/blink/renderer/core/html/parser/xss_auditor.cc
+++ b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
@@ -438,7 +438,7 @@
     }
     if (xss_protection_header == kReflectedXSSInvalid) {
       document->AddConsoleMessage(ConsoleMessage::Create(
-          kSecurityMessageSource, kErrorMessageLevel,
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
           "Error parsing header X-XSS-Protection: " + header_value + ": " +
               error_details + " at character position " +
               String::Format("%u", error_position) +
diff --git a/third_party/blink/renderer/core/html/parser/xss_auditor_delegate.cc b/third_party/blink/renderer/core/html/parser/xss_auditor_delegate.cc
index 240cc29..4cf4f30 100644
--- a/third_party/blink/renderer/core/html/parser/xss_auditor_delegate.cc
+++ b/third_party/blink/renderer/core/html/parser/xss_auditor_delegate.cc
@@ -106,7 +106,8 @@
                                    : WebFeature::kXSSAuditorBlockedScript);
 
   document_->AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kErrorMessageLevel, xss_info.BuildConsoleError()));
+      kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+      xss_info.BuildConsoleError()));
 
   LocalFrame* local_frame = document_->GetFrame();
   FrameLoader& frame_loader = local_frame->Loader();
diff --git a/third_party/blink/renderer/core/inspector/console_message.cc b/third_party/blink/renderer/core/inspector/console_message.cc
index 3a790f7..e7843eaf 100644
--- a/third_party/blink/renderer/core/inspector/console_message.cc
+++ b/third_party/blink/renderer/core/inspector/console_message.cc
@@ -19,7 +19,7 @@
 // static
 ConsoleMessage* ConsoleMessage::CreateForRequest(
     MessageSource source,
-    MessageLevel level,
+    mojom::ConsoleMessageLevel level,
     const String& message,
     const String& url,
     DocumentLoader* loader,
@@ -34,7 +34,7 @@
 // static
 ConsoleMessage* ConsoleMessage::Create(
     MessageSource source,
-    MessageLevel level,
+    mojom::ConsoleMessageLevel level,
     const String& message,
     std::unique_ptr<SourceLocation> location) {
   return MakeGarbageCollected<ConsoleMessage>(source, level, message,
@@ -43,7 +43,7 @@
 
 // static
 ConsoleMessage* ConsoleMessage::Create(MessageSource source,
-                                       MessageLevel level,
+                                       mojom::ConsoleMessageLevel level,
                                        const String& message) {
   return ConsoleMessage::Create(source, level, message,
                                 SourceLocation::Capture());
@@ -51,7 +51,7 @@
 
 // static
 ConsoleMessage* ConsoleMessage::CreateFromWorker(
-    MessageLevel level,
+    mojom::ConsoleMessageLevel level,
     const String& message,
     std::unique_ptr<SourceLocation> location,
     WorkerThread* worker_thread) {
@@ -65,28 +65,12 @@
 ConsoleMessage* ConsoleMessage::CreateFromWebConsoleMessage(
     const WebConsoleMessage& message,
     LocalFrame* local_frame) {
-  MessageLevel web_core_message_level = kInfoMessageLevel;
-  switch (message.level) {
-    case mojom::ConsoleMessageLevel::kVerbose:
-      web_core_message_level = kVerboseMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kInfo:
-      web_core_message_level = kInfoMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kWarning:
-      web_core_message_level = kWarningMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kError:
-      web_core_message_level = kErrorMessageLevel;
-      break;
-  }
-
   MessageSource message_source = message.nodes.empty()
                                      ? kOtherMessageSource
                                      : kRecommendationMessageSource;
 
   ConsoleMessage* console_message = ConsoleMessage::Create(
-      message_source, web_core_message_level, message.text,
+      message_source, message.level, message.text,
       SourceLocation::Create(message.url, message.line_number,
                              message.column_number, nullptr));
 
@@ -101,7 +85,7 @@
 }
 
 ConsoleMessage::ConsoleMessage(MessageSource source,
-                               MessageLevel level,
+                               mojom::ConsoleMessageLevel level,
                                const String& message,
                                std::unique_ptr<SourceLocation> location)
     : source_(source),
@@ -129,7 +113,7 @@
   return source_;
 }
 
-MessageLevel ConsoleMessage::Level() const {
+mojom::ConsoleMessageLevel ConsoleMessage::Level() const {
   return level_;
 }
 
@@ -161,9 +145,4 @@
   visitor->Trace(frame_);
 }
 
-STATIC_ASSERT_ENUM(mojom::ConsoleMessageLevel::kVerbose, kVerboseMessageLevel);
-STATIC_ASSERT_ENUM(mojom::ConsoleMessageLevel::kInfo, kInfoMessageLevel);
-STATIC_ASSERT_ENUM(mojom::ConsoleMessageLevel::kWarning, kWarningMessageLevel);
-STATIC_ASSERT_ENUM(mojom::ConsoleMessageLevel::kError, kErrorMessageLevel);
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/console_message.h b/third_party/blink/renderer/core/inspector/console_message.h
index 8c46275..99fced4 100644
--- a/third_party/blink/renderer/core/inspector/console_message.h
+++ b/third_party/blink/renderer/core/inspector/console_message.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_CONSOLE_MESSAGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_CONSOLE_MESSAGE_H_
 
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/inspector/console_types.h"
@@ -25,25 +26,25 @@
  public:
   // Location must be non-null.
   static ConsoleMessage* Create(MessageSource,
-                                MessageLevel,
+                                mojom::ConsoleMessageLevel,
                                 const String& message,
                                 std::unique_ptr<SourceLocation>);
 
   // Shortcut when location is unknown. Captures current location.
   static ConsoleMessage* Create(MessageSource,
-                                MessageLevel,
+                                mojom::ConsoleMessageLevel,
                                 const String& message);
 
   // This method captures current location if available.
   static ConsoleMessage* CreateForRequest(MessageSource,
-                                          MessageLevel,
+                                          mojom::ConsoleMessageLevel,
                                           const String& message,
                                           const String& url,
                                           DocumentLoader*,
                                           unsigned long request_identifier);
 
   // This creates message from WorkerMessageSource.
-  static ConsoleMessage* CreateFromWorker(MessageLevel,
+  static ConsoleMessage* CreateFromWorker(mojom::ConsoleMessageLevel,
                                           const String& message,
                                           std::unique_ptr<SourceLocation>,
                                           WorkerThread*);
@@ -54,7 +55,7 @@
       LocalFrame* local_frame);
 
   ConsoleMessage(MessageSource,
-                 MessageLevel,
+                 mojom::ConsoleMessageLevel,
                  const String& message,
                  std::unique_ptr<SourceLocation>);
   ~ConsoleMessage();
@@ -63,7 +64,7 @@
   const String& RequestIdentifier() const;
   double Timestamp() const;
   MessageSource Source() const;
-  MessageLevel Level() const;
+  mojom::ConsoleMessageLevel Level() const;
   const String& Message() const;
   const String& WorkerId() const;
   LocalFrame* Frame() const;
@@ -74,7 +75,7 @@
 
  private:
   MessageSource source_;
-  MessageLevel level_;
+  mojom::ConsoleMessageLevel level_;
   String message_;
   std::unique_ptr<SourceLocation> location_;
   String request_identifier_;
diff --git a/third_party/blink/renderer/core/inspector/console_message_storage.cc b/third_party/blink/renderer/core/inspector/console_message_storage.cc
index d483ab7..75f14aa 100644
--- a/third_party/blink/renderer/core/inspector/console_message_storage.cc
+++ b/third_party/blink/renderer/core/inspector/console_message_storage.cc
@@ -52,7 +52,7 @@
   // Change in this function requires adjustment of Catapult/Telemetry metric
   // tracing/tracing/metrics/console_error_metric.html.
   // See https://crbug.com/880432
-  if (message->Level() == kErrorMessageLevel) {
+  if (message->Level() == mojom::ConsoleMessageLevel::kError) {
     TRACE_EVENT_INSTANT1("blink.console", "ConsoleMessage::Error",
                          TRACE_EVENT_SCOPE_THREAD, "source",
                          MessageSourceToString(message->Source()));
diff --git a/third_party/blink/renderer/core/inspector/console_types.h b/third_party/blink/renderer/core/inspector/console_types.h
index 8cf77b6..0598949 100644
--- a/third_party/blink/renderer/core/inspector/console_types.h
+++ b/third_party/blink/renderer/core/inspector/console_types.h
@@ -23,13 +23,6 @@
   kInterventionMessageSource,
   kRecommendationMessageSource
 };
-
-enum MessageLevel {
-  kVerboseMessageLevel,
-  kInfoMessageLevel,
-  kWarningMessageLevel,
-  kErrorMessageLevel
-};
 }
 
 #endif  // !defined(ConsoleTypes_h)
diff --git a/third_party/blink/renderer/core/inspector/inspector_log_agent.cc b/third_party/blink/renderer/core/inspector/inspector_log_agent.cc
index bec90bb..dd50a9c82d 100644
--- a/third_party/blink/renderer/core/inspector/inspector_log_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_log_agent.cc
@@ -52,15 +52,15 @@
   }
 }
 
-String MessageLevelValue(MessageLevel level) {
+String MessageLevelValue(mojom::ConsoleMessageLevel level) {
   switch (level) {
-    case kVerboseMessageLevel:
+    case mojom::ConsoleMessageLevel::kVerbose:
       return protocol::Log::LogEntry::LevelEnum::Verbose;
-    case kInfoMessageLevel:
+    case mojom::ConsoleMessageLevel::kInfo:
       return protocol::Log::LogEntry::LevelEnum::Info;
-    case kWarningMessageLevel:
+    case mojom::ConsoleMessageLevel::kWarning:
       return protocol::Log::LogEntry::LevelEnum::Warning;
-    case kErrorMessageLevel:
+    case mojom::ConsoleMessageLevel::kError:
       return protocol::Log::LogEntry::LevelEnum::Error;
   }
   return protocol::Log::LogEntry::LevelEnum::Info;
@@ -256,7 +256,8 @@
       "Forced reflow while executing JavaScript took %" PRId64 "ms",
       duration.InMilliseconds());
   ConsoleMessage* message = ConsoleMessage::Create(
-      kViolationMessageSource, kVerboseMessageLevel, message_text);
+      kViolationMessageSource, mojom::ConsoleMessageLevel::kVerbose,
+      message_text);
   ConsoleMessageAdded(message);
 }
 
@@ -265,7 +266,8 @@
                                                base::TimeDelta time,
                                                SourceLocation* location) {
   ConsoleMessage* message = ConsoleMessage::Create(
-      kViolationMessageSource, kVerboseMessageLevel, text, location->Clone());
+      kViolationMessageSource, mojom::ConsoleMessageLevel::kVerbose, text,
+      location->Clone());
   ConsoleMessageAdded(message);
 }
 
diff --git a/third_party/blink/renderer/core/inspector/main_thread_debugger.cc b/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
index dccc070..763e05c9 100644
--- a/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/main_thread_debugger.cc
@@ -105,7 +105,7 @@
 
 void MainThreadDebugger::ReportConsoleMessage(ExecutionContext* context,
                                               MessageSource source,
-                                              MessageLevel level,
+                                              mojom::ConsoleMessageLevel level,
                                               const String& message,
                                               SourceLocation* location) {
   if (LocalFrame* frame = ToFrame(context))
@@ -188,9 +188,9 @@
     NOTREACHED();
   }
 
-  frame->Console().ReportMessageToClient(kJSMessageSource, kErrorMessageLevel,
-                                         event->MessageForConsole(),
-                                         event->Location());
+  frame->Console().ReportMessageToClient(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+      event->MessageForConsole(), event->Location());
 
   const String default_message = "Uncaught";
   if (script_state && script_state->ContextIsValid()) {
diff --git a/third_party/blink/renderer/core/inspector/main_thread_debugger.h b/third_party/blink/renderer/core/inspector/main_thread_debugger.h
index 43a8e4b4..4f6ecca 100644
--- a/third_party/blink/renderer/core/inspector/main_thread_debugger.h
+++ b/third_party/blink/renderer/core/inspector/main_thread_debugger.h
@@ -81,7 +81,7 @@
  private:
   void ReportConsoleMessage(ExecutionContext*,
                             MessageSource,
-                            MessageLevel,
+                            mojom::ConsoleMessageLevel,
                             const String& message,
                             SourceLocation*) override;
   int ContextGroupId(ExecutionContext*) override;
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.cc b/third_party/blink/renderer/core/inspector/thread_debugger.cc
index d0db380..bab91993 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.cc
@@ -48,23 +48,23 @@
 }
 
 // static
-MessageLevel ThreadDebugger::V8MessageLevelToMessageLevel(
+mojom::ConsoleMessageLevel ThreadDebugger::V8MessageLevelToMessageLevel(
     v8::Isolate::MessageErrorLevel level) {
-  MessageLevel result = kInfoMessageLevel;
+  mojom::ConsoleMessageLevel result = mojom::ConsoleMessageLevel::kInfo;
   switch (level) {
     case v8::Isolate::kMessageDebug:
-      result = kVerboseMessageLevel;
+      result = mojom::ConsoleMessageLevel::kVerbose;
       break;
     case v8::Isolate::kMessageWarning:
-      result = kWarningMessageLevel;
+      result = mojom::ConsoleMessageLevel::kWarning;
       break;
     case v8::Isolate::kMessageError:
-      result = kErrorMessageLevel;
+      result = mojom::ConsoleMessageLevel::kError;
       break;
     case v8::Isolate::kMessageLog:
     case v8::Isolate::kMessageInfo:
     default:
-      result = kInfoMessageLevel;
+      result = mojom::ConsoleMessageLevel::kInfo;
       break;
   }
   return result;
@@ -136,7 +136,8 @@
     message = message.Substring(0, 8) + " (in promise)" + message.Substring(8);
 
   ReportConsoleMessage(ToExecutionContext(context), kJSMessageSource,
-                       kErrorMessageLevel, message, location.get());
+                       mojom::ConsoleMessageLevel::kError, message,
+                       location.get());
   String url = location->Url();
   return GetV8Inspector()->exceptionThrown(
       context, ToV8InspectorStringView(default_message), exception,
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.h b/third_party/blink/renderer/core/inspector/thread_debugger.h
index 6550cee..685a84c3 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.h
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include "base/macros.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
 #include "third_party/blink/renderer/core/inspector/console_types.h"
@@ -61,7 +62,7 @@
   virtual int ContextGroupId(ExecutionContext*) = 0;
   virtual void ReportConsoleMessage(ExecutionContext*,
                                     MessageSource,
-                                    MessageLevel,
+                                    mojom::ConsoleMessageLevel,
                                     const String& message,
                                     SourceLocation*) = 0;
   void installAdditionalCommandLineAPI(v8::Local<v8::Context>,
@@ -76,7 +77,7 @@
                                                    v8::Local<v8::Array>,
                                                    int index,
                                                    v8::Local<v8::Value>);
-  static MessageLevel V8MessageLevelToMessageLevel(
+  static mojom::ConsoleMessageLevel V8MessageLevelToMessageLevel(
       v8::Isolate::MessageErrorLevel);
 
   v8::Isolate* isolate_;
diff --git a/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc b/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
index 01bbf16f..7e362aa 100644
--- a/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/worker_thread_debugger.cc
@@ -71,11 +71,12 @@
   DCHECK(worker_threads_.IsEmpty());
 }
 
-void WorkerThreadDebugger::ReportConsoleMessage(ExecutionContext* context,
-                                                MessageSource source,
-                                                MessageLevel level,
-                                                const String& message,
-                                                SourceLocation* location) {
+void WorkerThreadDebugger::ReportConsoleMessage(
+    ExecutionContext* context,
+    MessageSource source,
+    mojom::ConsoleMessageLevel level,
+    const String& message,
+    SourceLocation* location) {
   if (!context)
     return;
   To<WorkerOrWorkletGlobalScope>(context)
@@ -132,8 +133,8 @@
 void WorkerThreadDebugger::ExceptionThrown(WorkerThread* worker_thread,
                                            ErrorEvent* event) {
   worker_thread->GetWorkerReportingProxy().ReportConsoleMessage(
-      kJSMessageSource, kErrorMessageLevel, event->MessageForConsole(),
-      event->Location());
+      kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+      event->MessageForConsole(), event->Location());
 
   const String default_message = "Uncaught";
   ScriptState* script_state =
diff --git a/third_party/blink/renderer/core/inspector/worker_thread_debugger.h b/third_party/blink/renderer/core/inspector/worker_thread_debugger.h
index 94ea18b..dce0ddd 100644
--- a/third_party/blink/renderer/core/inspector/worker_thread_debugger.h
+++ b/third_party/blink/renderer/core/inspector/worker_thread_debugger.h
@@ -64,7 +64,7 @@
   int ContextGroupId(ExecutionContext*) override;
   void ReportConsoleMessage(ExecutionContext*,
                             MessageSource,
-                            MessageLevel,
+                            mojom::ConsoleMessageLevel,
                             const String& message,
                             SourceLocation*) override;
 
diff --git a/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc b/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc
index 9390b052..dd21a6c 100644
--- a/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc
+++ b/third_party/blink/renderer/core/layout/custom/css_layout_definition.cc
@@ -159,7 +159,7 @@
       CustomLayoutFragment* fragment = fragment_request->PerformLayout(isolate);
       if (!fragment) {
         execution_context->AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kInfoMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
             "Unable to perform layout request due to an invalid child, "
             "falling back to block layout."));
         return false;
@@ -186,7 +186,7 @@
 
         if (!fragment) {
           execution_context->AddConsoleMessage(ConsoleMessage::Create(
-              kJSMessageSource, kInfoMessageLevel,
+              kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
               "Unable to perform layout request due to an invalid child, "
               "falling back to block layout."));
           return false;
@@ -208,10 +208,10 @@
 
     // We recieved something that wasn't either a CustomLayoutFragmentRequest,
     // or a sequence of CustomLayoutFragmentRequests. Fallback to block layout.
-    execution_context->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kInfoMessageLevel,
-                               "Unable to parse the layout request, "
-                               "falling back to block layout."));
+    execution_context->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+        "Unable to parse the layout request, "
+        "falling back to block layout."));
     return false;
   }
 
@@ -231,10 +231,10 @@
   if (exception_state.HadException()) {
     V8ScriptRunner::ReportException(isolate, exception_state.GetException());
     exception_state.ClearException();
-    execution_context->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kInfoMessageLevel,
-                               "Unable to parse the layout function "
-                               "result, falling back to block layout."));
+    execution_context->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+        "Unable to parse the layout function "
+        "result, falling back to block layout."));
     return false;
   }
 
@@ -253,10 +253,10 @@
   if (exception_state.HadException()) {
     V8ScriptRunner::ReportException(isolate, exception_state.GetException());
     exception_state.ClearException();
-    execution_context->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kInfoMessageLevel,
-                               "Unable to serialize the data provided in the "
-                               "result, falling back to block layout."));
+    execution_context->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+        "Unable to serialize the data provided in the "
+        "result, falling back to block layout."));
     return false;
   }
 
@@ -274,7 +274,7 @@
   V8ScriptRunner::ReportException(isolate, exception_state->GetException());
   exception_state->ClearException();
   execution_context->AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kInfoMessageLevel,
+      kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
       "The layout function failed, falling back to block layout."));
 }
 
diff --git a/third_party/blink/renderer/core/layout/custom/layout_custom.cc b/third_party/blink/renderer/core/layout/custom/layout_custom.cc
index 611c30a..bdb310d 100644
--- a/third_party/blink/renderer/core/layout/custom/layout_custom.cc
+++ b/third_party/blink/renderer/core/layout/custom/layout_custom.cc
@@ -165,7 +165,7 @@
 
     if (!instance_) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kInfoMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
           "Unable to create an instance of layout class '" + name +
               "', falling back to block layout."));
       return false;
@@ -194,18 +194,18 @@
       }
 
       if (index >= child_fragments.size()) {
-        GetDocument().AddConsoleMessage(
-            ConsoleMessage::Create(kJSMessageSource, kInfoMessageLevel,
-                                   "Chrome currently requires exactly one "
-                                   "LayoutFragment per LayoutChild, "
-                                   "falling back to block layout."));
+        GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+            kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+            "Chrome currently requires exactly one "
+            "LayoutFragment per LayoutChild, "
+            "falling back to block layout."));
         return false;
       }
 
       CustomLayoutFragment* fragment = child_fragments[index++];
       if (!fragment->IsValid()) {
         GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kInfoMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
             "An invalid LayoutFragment was returned from the "
             "layout, falling back to block layout."));
         return false;
@@ -244,11 +244,11 @@
 
     // Currently we only support exactly one LayoutFragment per LayoutChild.
     if (index != child_fragments.size()) {
-      GetDocument().AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kInfoMessageLevel,
-                                 "Chrome currently requires exactly one "
-                                 "LayoutFragment per LayoutChild, "
-                                 "falling back to block layout."));
+      GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+          kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+          "Chrome currently requires exactly one "
+          "LayoutFragment per LayoutChild, "
+          "falling back to block layout."));
       return false;
     }
 
diff --git a/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc b/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc
index a0dbf33..47e6c34 100644
--- a/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc
+++ b/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc
@@ -151,9 +151,9 @@
 
   const KURL& url = image_resource.Url();
   String url_string = url.IsNull() ? "''" : url.ElidedString();
-  document.AddConsoleMessage(
-      ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel,
-                             "Unsafe attempt to load URL " + url_string + "."));
+  document.AddConsoleMessage(ConsoleMessage::Create(
+      kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
+      "Unsafe attempt to load URL " + url_string + "."));
 
   return false;
 }
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.cc b/third_party/blink/renderer/core/loader/base_fetch_context.cc
index 4d814a0..ecddbdf 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.cc
@@ -230,8 +230,8 @@
               ". Domains, protocols and ports must match.\n";
   }
 
-  AddConsoleMessage(ConsoleMessage::Create(kSecurityMessageSource,
-                                           kErrorMessageLevel, message));
+  AddConsoleMessage(ConsoleMessage::Create(
+      kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message));
 }
 
 base::Optional<ResourceRequestBlockedReason>
@@ -299,7 +299,7 @@
       !origin->CanDisplay(url)) {
     if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
       AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kErrorMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kError,
           "Not allowed to load local resource: " + url.GetString()));
     }
     RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::requestResource URL was not "
diff --git a/third_party/blink/renderer/core/loader/console_logger_impl_base.cc b/third_party/blink/renderer/core/loader/console_logger_impl_base.cc
index decf906..723be39 100644
--- a/third_party/blink/renderer/core/loader/console_logger_impl_base.cc
+++ b/third_party/blink/renderer/core/loader/console_logger_impl_base.cc
@@ -15,19 +15,22 @@
 void ConsoleLoggerImplBase::AddInfoMessage(Source source,
                                            const String& message) {
   AddConsoleMessage(ConsoleMessage::Create(GetMessageSourceFromSource(source),
-                                           kInfoMessageLevel, message));
+                                           mojom::ConsoleMessageLevel::kInfo,
+                                           message));
 }
 
 void ConsoleLoggerImplBase::AddWarningMessage(Source source,
                                               const String& message) {
   AddConsoleMessage(ConsoleMessage::Create(GetMessageSourceFromSource(source),
-                                           kWarningMessageLevel, message));
+                                           mojom::ConsoleMessageLevel::kWarning,
+                                           message));
 }
 
 void ConsoleLoggerImplBase::AddErrorMessage(Source source,
                                             const String& message) {
   AddConsoleMessage(ConsoleMessage::Create(GetMessageSourceFromSource(source),
-                                           kErrorMessageLevel, message));
+                                           mojom::ConsoleMessageLevel::kError,
+                                           message));
 }
 
 MessageSource ConsoleLoggerImplBase::GetMessageSourceFromSource(Source source) {
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index cc12719..ab0914f 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -540,7 +540,7 @@
       navigation_timing_info_->AddFinalTransferSize(
           total_encoded_data_length == -1 ? 0 : total_encoded_data_length);
       if (response_.HttpStatusCode() < 400 && report_timing_info_to_parent_) {
-        navigation_timing_info_->SetLoadFinishTime(completion_time);
+        navigation_timing_info_->SetLoadResponseEnd(completion_time);
         if (state_ >= kCommitted) {
           // Note that we currently lose timing info for empty documents,
           // which will be fixed with synchronous commit.
@@ -777,7 +777,7 @@
                          "required by its embedder: '" +
                          GetFrameLoader().RequiredCSP() + "'.";
         ConsoleMessage* console_message = ConsoleMessage::CreateForRequest(
-            kSecurityMessageSource, kErrorMessageLevel, message,
+            kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message,
             response.CurrentRequestUrl(), this, MainResourceIdentifier());
         frame_->GetDocument()->AddConsoleMessage(console_message);
         return nullptr;
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 2189154..1aa3ce1 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -120,7 +120,7 @@
   }
   void AddMessageToConsole(LocalFrame*,
                            MessageSource,
-                           MessageLevel,
+                           mojom::ConsoleMessageLevel,
                            const String&,
                            unsigned,
                            const String&,
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index d00ee83..3291c84 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -1289,7 +1289,7 @@
   if (document && document->IsFreezingInProgress() &&
       !resource_request.GetKeepalive()) {
     AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kErrorMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
         "Only fetch keepalive is allowed during onfreeze: " + url.GetString()));
     return ResourceRequestBlockedReason::kOther;
   }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 6028d4d..300738c 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -714,7 +714,7 @@
 
   if (!request.OriginDocument()->GetSecurityOrigin()->CanDisplay(url)) {
     request.OriginDocument()->AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Not allowed to load local resource: " + url.ElidedString()));
     return false;
   }
@@ -734,7 +734,7 @@
        (url.ProtocolIsData() &&
         network_utils::IsDataURLMimeTypeSupported(url)))) {
     frame_->GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Not allowed to navigate top frame to " + url.Protocol() +
             " URL: " + url.ElidedString()));
     return false;
diff --git a/third_party/blink/renderer/core/loader/http_equiv.cc b/third_party/blink/renderer/core/loader/http_equiv.cc
index 3af0d588..f5e8f4b 100644
--- a/third_party/blink/renderer/core/loader/http_equiv.cc
+++ b/third_party/blink/renderer/core/loader/http_equiv.cc
@@ -105,7 +105,7 @@
     document.ParseDNSPrefetchControlHeader(content);
   } else if (EqualIgnoringASCIICase(equiv, "x-frame-options")) {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "X-Frame-Options may only be set via an HTTP header sent along with a "
         "document. It may not be set inside <meta>."));
   } else if (EqualIgnoringASCIICase(equiv, http_names::kAcceptCH)) {
@@ -207,7 +207,7 @@
   }
 
   document.AddConsoleMessage(ConsoleMessage::Create(
-      kSecurityMessageSource, kErrorMessageLevel,
+      kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
       String::Format("Blocked setting the `%s` cookie from a `<meta>` tag.",
                      content.Utf8().data())));
 }
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.cc b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
index 6acbcae..5dea1e6 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -284,8 +284,9 @@
       allowed ? "This content should also be served over HTTPS."
               : "This request has been blocked; the content must be served "
                 "over HTTPS.");
-  MessageLevel message_level =
-      allowed ? kWarningMessageLevel : kErrorMessageLevel;
+  mojom::ConsoleMessageLevel message_level =
+      allowed ? mojom::ConsoleMessageLevel::kWarning
+              : mojom::ConsoleMessageLevel::kError;
   if (source_location) {
     return ConsoleMessage::Create(kSecurityMessageSource, message_level,
                                   message, std::move(source_location));
@@ -547,8 +548,9 @@
                 "deprecated."
               : "This request has been blocked; this endpoint must be "
                 "available over WSS.");
-  MessageLevel message_level =
-      allowed ? kWarningMessageLevel : kErrorMessageLevel;
+  mojom::ConsoleMessageLevel message_level =
+      allowed ? mojom::ConsoleMessageLevel::kWarning
+              : mojom::ConsoleMessageLevel::kError;
   return ConsoleMessage::Create(kSecurityMessageSource, message_level, message);
 }
 
@@ -653,7 +655,7 @@
         MainResourceUrlForFrame(mixed_frame).ElidedString().Utf8().data(),
         url.ElidedString().Utf8().data());
     frame->GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kWarningMessageLevel, message));
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
   }
 
   return true;
@@ -782,8 +784,8 @@
       "autougprade-mixed.md",
       main_resource_url.ElidedString().Utf8().data(),
       mixed_content_url.ElidedString().Utf8().data());
-  return ConsoleMessage::Create(kSecurityMessageSource, kWarningMessageLevel,
-                                message);
+  return ConsoleMessage::Create(kSecurityMessageSource,
+                                mojom::ConsoleMessageLevel::kWarning, message);
 }
 
 // static
@@ -800,8 +802,8 @@
       "autougprade-mixed.md",
       main_resource_url.ElidedString().Utf8().data(),
       mixed_content_url.ElidedString().Utf8().data());
-  return ConsoleMessage::Create(kSecurityMessageSource, kWarningMessageLevel,
-                                message);
+  return ConsoleMessage::Create(kSecurityMessageSource,
+                                mojom::ConsoleMessageLevel::kWarning, message);
 }
 
 WebMixedContentContextType MixedContentChecker::ContextTypeForInspector(
diff --git a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
index 0245b87e..17720e2 100644
--- a/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/document_module_script_fetcher.cc
@@ -66,8 +66,8 @@
   if (source_text.IsNull()) {
     HeapVector<Member<ConsoleMessage>> error_messages;
     error_messages.push_back(ConsoleMessage::CreateForRequest(
-        kJSMessageSource, kErrorMessageLevel, "Unexpected data error",
-        fetch_params.Url().GetString(), nullptr, 0));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+        "Unexpected data error", fetch_params.Url().GetString(), nullptr, 0));
     client_->NotifyFetchFinished(base::nullopt, error_messages);
     return true;
   }
diff --git a/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
index 6cc54cdc..09e3390 100644
--- a/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/installed_service_worker_module_script_fetcher.cc
@@ -32,7 +32,7 @@
   if (!script) {
     HeapVector<Member<ConsoleMessage>> error_messages;
     error_messages.push_back(ConsoleMessage::CreateForRequest(
-        kJSMessageSource, kErrorMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
         "Failed to load the script unexpectedly",
         fetch_params.Url().GetString(), nullptr, 0));
     client->NotifyFetchFinished(base::nullopt, error_messages);
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc
index da86ef0..dcec0d8 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_fetcher.cc
@@ -63,7 +63,7 @@
         "\". Strict MIME type checking is enforced for module scripts per "
         "HTML spec.";
     error_messages->push_back(ConsoleMessage::CreateForRequest(
-        kJSMessageSource, kErrorMessageLevel, message,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError, message,
         response.CurrentRequestUrl().GetString(), nullptr,
         resource->Identifier()));
     return false;
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index 2a508a0..457ef64 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -72,7 +72,7 @@
         !global_scope_->GetSecurityOrigin()->IsSameSchemeHostPort(
             SecurityOrigin::Create(response_url).get())) {
       error_messages.push_back(ConsoleMessage::Create(
-          kSecurityMessageSource, kErrorMessageLevel,
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
           "Refused to cross-origin redirects of the top-level worker script."));
       client_->NotifyFetchFinished(base::nullopt, error_messages);
       return;
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index 7b73208..ae7fe2e 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -117,7 +117,7 @@
       if (settings->GetLogDnsPrefetchAndPreconnect()) {
         SendMessageToConsoleForPossiblyNullDocument(
             ConsoleMessage::Create(
-                kOtherMessageSource, kVerboseMessageLevel,
+                kOtherMessageSource, mojom::ConsoleMessageLevel::kVerbose,
                 String("DNS prefetch triggered for " + params.href.Host())),
             document, frame);
       }
@@ -141,12 +141,13 @@
     if (settings && settings->GetLogDnsPrefetchAndPreconnect()) {
       SendMessageToConsoleForPossiblyNullDocument(
           ConsoleMessage::Create(
-              kOtherMessageSource, kVerboseMessageLevel,
+              kOtherMessageSource, mojom::ConsoleMessageLevel::kVerbose,
               String("Preconnect triggered for ") + params.href.GetString()),
           document, frame);
       if (params.cross_origin != kCrossOriginAttributeNotSet) {
         SendMessageToConsoleForPossiblyNullDocument(
-            ConsoleMessage::Create(kOtherMessageSource, kVerboseMessageLevel,
+            ConsoleMessage::Create(kOtherMessageSource,
+                                   mojom::ConsoleMessageLevel::kVerbose,
                                    String("Preconnect CORS setting is ") +
                                        String((params.cross_origin ==
                                                kCrossOriginAttributeAnonymous)
@@ -225,7 +226,7 @@
   UseCounter::Count(document, WebFeature::kLinkRelPreload);
   if (!url.IsValid() || url.IsEmpty()) {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         String("<link rel=preload> has an invalid `href` value")));
     return nullptr;
   }
@@ -242,14 +243,14 @@
     UseCounter::Count(document, WebFeature::kLinkHeaderPreload);
   if (resource_type == base::nullopt) {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         String("<link rel=preload> must have a valid `as` value")));
     return nullptr;
   }
 
   if (!IsSupportedType(resource_type.value(), params.type)) {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         String("<link rel=preload> has an unsupported `type` value")));
     return nullptr;
   }
@@ -276,7 +277,7 @@
   Settings* settings = document.GetSettings();
   if (settings && settings->GetLogPreload()) {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kVerboseMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kVerbose,
         String("Preload triggered for " + url.Host() + url.GetPath())));
   }
   link_fetch_params.SetLinkPreload(true);
@@ -298,9 +299,9 @@
   // Step 1. "If the href attribute's value is the empty string, then return."
   // [spec text]
   if (params.href.IsEmpty()) {
-    document.AddConsoleMessage(
-        ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
-                               "<link rel=modulepreload> has no `href` value"));
+    document.AddConsoleMessage(ConsoleMessage::Create(
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "<link rel=modulepreload> has no `href` value"));
     return;
   }
 
@@ -323,7 +324,7 @@
   // Currently we only support as="script".
   if (!params.as.IsEmpty() && params.as != "script") {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         String("<link rel=modulepreload> has an invalid `as` value " +
                params.as)));
     // This triggers the same logic as Step 11 asynchronously, which will fire
@@ -343,7 +344,7 @@
   // |href| is already resolved in caller side.
   if (!params.href.IsValid()) {
     document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "<link rel=modulepreload> has an invalid `href` value " +
             params.href.GetString()));
     return;
@@ -405,10 +406,10 @@
 
   Settings* settings = document.GetSettings();
   if (settings && settings->GetLogPreload()) {
-    document.AddConsoleMessage(
-        ConsoleMessage::Create(kOtherMessageSource, kVerboseMessageLevel,
-                               "Module preload triggered for " +
-                                   params.href.Host() + params.href.GetPath()));
+    document.AddConsoleMessage(ConsoleMessage::Create(
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kVerbose,
+        "Module preload triggered for " + params.href.Host() +
+            params.href.GetPath()));
   }
 
   // Asynchronously continue processing after
diff --git a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
index 68c172e..f228a5b 100644
--- a/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
+++ b/third_party/blink/renderer/core/loader/previews_resource_loading_hints.cc
@@ -129,9 +129,9 @@
 
 void PreviewsResourceLoadingHints::ReportBlockedLoading(
     const KURL& resource_url) const {
-  execution_context_->AddConsoleMessage(
-      ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
-                             GetConsoleLogStringForBlockedLoad(resource_url)));
+  execution_context_->AddConsoleMessage(ConsoleMessage::Create(
+      kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      GetConsoleLogStringForBlockedLoad(resource_url)));
 }
 
 void PreviewsResourceLoadingHints::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.cc b/third_party/blink/renderer/core/loader/resource/image_resource.cc
index 20373d7..604990b 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.cc
@@ -89,6 +89,9 @@
 
  private:
   const KURL& Url() const override { return resource_->Url(); }
+  TimeTicks LoadResponseEnd() const override {
+    return resource_->LoadResponseEnd();
+  }
   bool IsSchedulingReload() const override {
     return resource_->is_scheduling_reload_;
   }
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
index 05d7b57..b2a1d1f3 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -40,6 +40,7 @@
 
  private:
   const KURL& Url() const override { return url_; }
+  TimeTicks LoadResponseEnd() const override { return TimeTicks(); }
   bool IsSchedulingReload() const override { return false; }
   const ResourceResponse& GetResponse() const override { return response_; }
   bool ShouldShowPlaceholder() const override { return false; }
@@ -615,6 +616,10 @@
   return info_->Url();
 }
 
+TimeTicks ImageResourceContent::LoadResponseEnd() const {
+  return info_->LoadResponseEnd();
+}
+
 bool ImageResourceContent::HasCacheControlNoStoreHeader() const {
   return info_->HasCacheControlNoStoreHeader();
 }
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
index e975f4e..bfc1c72c 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -107,6 +107,7 @@
 
   // Redirecting methods to Resource.
   const KURL& Url() const;
+  TimeTicks LoadResponseEnd() const;
   bool IsAccessAllowed();
   const ResourceResponse& GetResponse() const;
   base::Optional<ResourceError> GetResourceError() const;
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_info.h b/third_party/blink/renderer/core/loader/resource/image_resource_info.h
index 1e8c7d5..f08d66a 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_info.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_info.h
@@ -29,6 +29,7 @@
  public:
   ~ImageResourceInfo() = default;
   virtual const KURL& Url() const = 0;
+  virtual TimeTicks LoadResponseEnd() const = 0;
   virtual bool IsSchedulingReload() const = 0;
   virtual const ResourceResponse& GetResponse() const = 0;
   virtual bool ShouldShowPlaceholder() const = 0;
diff --git a/third_party/blink/renderer/core/loader/subresource_filter.cc b/third_party/blink/renderer/core/loader/subresource_filter.cc
index 1c7168a5..fe9566a 100644
--- a/third_party/blink/renderer/core/loader/subresource_filter.cc
+++ b/third_party/blink/renderer/core/loader/subresource_filter.cc
@@ -130,7 +130,7 @@
       // warning in Lighthouse.
       if (subresource_filter_->ShouldLogToConsole()) {
         execution_context_->AddConsoleMessage(ConsoleMessage::Create(
-            kOtherMessageSource, kErrorMessageLevel,
+            kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
             GetErrorStringForDisallowedLoad(resource_url)));
       }
       FALLTHROUGH;
diff --git a/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc b/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc
index eab35b2..292b52c8d 100644
--- a/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc
+++ b/third_party/blink/renderer/core/loader/subresource_integrity_helper.cc
@@ -56,8 +56,8 @@
     HeapVector<Member<ConsoleMessage>>* messages) {
   DCHECK(messages);
   for (const auto& message : report_info.ConsoleErrorMessages()) {
-    messages->push_back(ConsoleMessage::Create(kSecurityMessageSource,
-                                               kErrorMessageLevel, message));
+    messages->push_back(ConsoleMessage::Create(
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError, message));
   }
 }
 
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index d939cb2..36fde33 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -622,7 +622,7 @@
       ThreadableLoaderClient* client = client_;
       Clear();
       ConsoleMessage* message = ConsoleMessage::Create(
-          kNetworkMessageSource, kErrorMessageLevel,
+          kNetworkMessageSource, mojom::ConsoleMessageLevel::kError,
           "Failed to load resource: net::ERR_TOO_MANY_REDIRECTS",
           SourceLocation::Capture(original_url, 0, 0));
       execution_context_->AddConsoleMessage(message);
@@ -1030,7 +1030,8 @@
         *GetSecurityOrigin(), ResourceType::kRaw,
         resource_loader_options_.initiator_info.name);
     execution_context_->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kErrorMessageLevel, std::move(message)));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+        std::move(message)));
   }
   Resource* resource = GetResource();
   if (resource)
diff --git a/third_party/blink/renderer/core/page/chrome_client.cc b/third_party/blink/renderer/core/page/chrome_client.cc
index e630fd49..3bdf7360 100644
--- a/third_party/blink/renderer/core/page/chrome_client.cc
+++ b/third_party/blink/renderer/core/page/chrome_client.cc
@@ -257,7 +257,7 @@
     UseCounter::Count(frame->GetDocument(),
                       WebFeature::kDialogInSandboxedContext);
     frame->Console().AddMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Ignored call to 'print()'. The document is sandboxed, and the "
         "'allow-modals' keyword is not set."));
     return false;
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 27de71c..0557ac8 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -30,6 +30,7 @@
 #include "cc/input/event_listener_properties.h"
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/public/platform/blame_context.h"
 #include "third_party/blink/public/platform/web_drag_operation.h"
 #include "third_party/blink/public/platform/web_focus_type.h"
@@ -38,7 +39,6 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/html/forms/popup_menu.h"
-#include "third_party/blink/renderer/core/inspector/console_types.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
@@ -179,7 +179,7 @@
                                                     const String& source) = 0;
   virtual void AddMessageToConsole(LocalFrame*,
                                    MessageSource,
-                                   MessageLevel,
+                                   mojom::ConsoleMessageLevel,
                                    const String& message,
                                    unsigned line_number,
                                    const String& source_id,
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 15910249..cb0aa30 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -309,7 +309,7 @@
 
 void ChromeClientImpl::AddMessageToConsole(LocalFrame* local_frame,
                                            MessageSource source,
-                                           MessageLevel level,
+                                           mojom::ConsoleMessageLevel level,
                                            const String& message,
                                            unsigned line_number,
                                            const String& source_id,
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index d3ae2e0..baccaaf 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -94,7 +94,7 @@
                                             const String&) override;
   void AddMessageToConsole(LocalFrame*,
                            MessageSource,
-                           MessageLevel,
+                           mojom::ConsoleMessageLevel,
                            const String& message,
                            unsigned line_number,
                            const String& source_id,
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index 85519fd..6ee6ab7 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -335,7 +335,7 @@
       // FIXME: This message should be moved off the console once a solution to
       // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
       opener_frame.GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-          kSecurityMessageSource, kErrorMessageLevel,
+          kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
           "Blocked opening '" +
               request.GetResourceRequest().Url().ElidedString() +
               "' in a new window because the request was made in a sandboxed "
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index 926b3bb..27bbf66 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -828,9 +828,9 @@
 
 void Page::ReportIntervention(const String& text) {
   if (LocalFrame* local_frame = DeprecatedLocalMainFrame()) {
-    ConsoleMessage* message =
-        ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel, text,
-                               SourceLocation::Create(String(), 0, 0, nullptr));
+    ConsoleMessage* message = ConsoleMessage::Create(
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning, text,
+        SourceLocation::Create(String(), 0, 0, nullptr));
     local_frame->GetDocument()->AddConsoleMessage(message);
   }
 }
diff --git a/third_party/blink/renderer/core/page/pointer_lock_controller.cc b/third_party/blink/renderer/core/page/pointer_lock_controller.cc
index 7ab0459..7ac1e69 100644
--- a/third_party/blink/renderer/core/page/pointer_lock_controller.cc
+++ b/third_party/blink/renderer/core/page/pointer_lock_controller.cc
@@ -61,7 +61,7 @@
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     target->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-        kSecurityMessageSource, kErrorMessageLevel,
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kError,
         "Blocked pointer lock on an element because the element's frame is "
         "sandboxed and the 'allow-pointer-lock' permission is not set."));
     EnqueueEvent(event_type_names::kPointerlockerror, target);
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.cc b/third_party/blink/renderer/core/paint/image_element_timing.cc
index 57eb727..d5ddb61 100644
--- a/third_party/blink/renderer/core/paint/image_element_timing.cc
+++ b/third_party/blink/renderer/core/paint/image_element_timing.cc
@@ -102,14 +102,9 @@
     return;
   }
 
-  // Compute the |name| for the entry. Use the 'elementtiming' attribute. If
-  // empty, use the ID. If empty, use 'img'.
-  AtomicString name = attr;
-  if (name.IsEmpty())
-    name = element->FastGetAttribute(html_names::kIdAttr);
-  if (name.IsEmpty())
-    name = "img";
-  element_timings_.emplace_back(name, visible_new_visual_rect);
+  element_timings_.emplace_back(AtomicString(cached_image->Url().GetString()),
+                                visible_new_visual_rect,
+                                cached_image->LoadResponseEnd(), attr);
   // Only queue a swap promise when |element_timings_| was empty. All of the
   // records in |element_timings_| will be processed when the promise succeeds
   // or fails, and at that time the vector is cleared.
@@ -128,7 +123,8 @@
                       performance->ShouldBufferEntries())) {
     for (const auto& element_timing : element_timings_) {
       performance->AddElementTiming(element_timing.name, element_timing.rect,
-                                    timestamp);
+                                    timestamp, element_timing.response_end,
+                                    element_timing.identifier);
     }
   }
   element_timings_.clear();
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.h b/third_party/blink/renderer/core/paint/image_element_timing.h
index 6c08c87..1c3b110 100644
--- a/third_party/blink/renderer/core/paint/image_element_timing.h
+++ b/third_party/blink/renderer/core/paint/image_element_timing.h
@@ -52,10 +52,19 @@
 
   // Struct containing information about image element timing.
   struct ElementTimingInfo {
+    ElementTimingInfo(const AtomicString& name,
+                      IntRect rect,
+                      TimeTicks response_end,
+                      const AtomicString& identifier)
+        : name(name),
+          rect(rect),
+          response_end(response_end),
+          identifier(identifier) {}
+
     AtomicString name;
     IntRect rect;
-    ElementTimingInfo(AtomicString name, IntRect rect)
-        : name(name), rect(rect) {}
+    TimeTicks response_end;
+    AtomicString identifier;
   };
   // Vector containing the element timing infos that will be reported during the
   // next swap promise callback.
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index 5cfbc3b..9504842 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -83,9 +83,9 @@
   // this fragment is at the end of line.
   if (selection_status.line_break == SelectSoftLineBreak::kNotSelected)
     return rect;
-  if (paint_fragment.GetLayoutObject()
-          ->ContainingNGBlockFlow()
-          ->ShouldTruncateOverflowingText())
+  LayoutBlockFlow* layout_block_flow =
+      paint_fragment.GetLayoutObject()->ContainingNGBlockFlow();
+  if (layout_block_flow && layout_block_flow->ShouldTruncateOverflowingText())
     return rect;
   // Copy from InlineTextBoxPainter::PaintSelection.
   const LayoutUnit space_width(paint_fragment.Style().GetFont().SpaceWidth());
diff --git a/third_party/blink/renderer/core/script/document_write_intervention.cc b/third_party/blink/renderer/core/script/document_write_intervention.cc
index 692610b..c9bfcd3 100644
--- a/third_party/blink/renderer/core/script/document_write_intervention.cc
+++ b/third_party/blink/renderer/core/script/document_write_intervention.cc
@@ -31,8 +31,8 @@
       "confirmed in a subsequent console message. "
       "See https://www.chromestatus.com/feature/5718547946799104 "
       "for more details.";
-  document.AddConsoleMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, message));
+  document.AddConsoleMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
   DVLOG(1) << message.Utf8().data();
 }
 
@@ -42,8 +42,8 @@
       ", invoked via document.write was NOT BLOCKED on this page load, but MAY "
       "be blocked by the browser in future page loads with poor network "
       "connectivity.";
-  document.AddConsoleMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, message));
+  document.AddConsoleMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
 }
 
 void EmitErrorBlocked(const String& url, Document& document) {
@@ -54,7 +54,7 @@
       ", invoked via document.write was BLOCKED by the browser due to poor "
       "network connectivity. ";
   document.AddConsoleMessage(ConsoleMessage::Create(
-      kInterventionMessageSource, kErrorMessageLevel, message));
+      kInterventionMessageSource, mojom::ConsoleMessageLevel::kError, message));
 }
 
 void AddWarningHeader(FetchParameters* params) {
diff --git a/third_party/blink/renderer/core/script/script_loader.cc b/third_party/blink/renderer/core/script/script_loader.cc
index 2fd42f0..689e83e9 100644
--- a/third_party/blink/renderer/core/script/script_loader.cc
+++ b/third_party/blink/renderer/core/script/script_loader.cc
@@ -185,16 +185,16 @@
 
   if (!modulator->IsAcquiringImportMaps()) {
     element_document.AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kErrorMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
         "An import map is added after module script load was triggered."));
     return ShouldFireErrorEvent::kShouldFire;
   }
 
   // TODO(crbug.com/922212): Implemenet external import maps.
   if (element.HasSourceAttribute()) {
-    element_document.AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel,
-                               "External import maps are not yet supported."));
+    element_document.AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+        "External import maps are not yet supported."));
     return ShouldFireErrorEvent::kShouldFire;
   }
 
@@ -408,7 +408,7 @@
   if (ShouldBlockSyncScriptForFeaturePolicy(element_.Get(), GetScriptType(),
                                             parser_inserted_)) {
     element_document.AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kErrorMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
         "Synchronous script execution is disabled by Feature Policy"));
     return false;
   }
diff --git a/third_party/blink/renderer/core/script/xml_parser_script_runner.cc b/third_party/blink/renderer/core/script/xml_parser_script_runner.cc
index 91340e90..f7b2ed8 100644
--- a/third_party/blink/renderer/core/script/xml_parser_script_runner.cc
+++ b/third_party/blink/renderer/core/script/xml_parser_script_runner.cc
@@ -79,10 +79,10 @@
   if (script_loader->GetScriptType() != mojom::ScriptType::kClassic) {
     // XMLDocumentParser does not support a module script, and thus ignores it.
     success = false;
-    document.AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel,
-                               "Module scripts in XML documents are currently "
-                               "not supported. See crbug.com/717643"));
+    document.AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+        "Module scripts in XML documents are currently "
+        "not supported. See crbug.com/717643"));
   }
 
   if (!success)
diff --git a/third_party/blink/renderer/core/svg/svg_element.cc b/third_party/blink/renderer/core/svg/svg_element.cc
index 3e64fed..98977d27 100644
--- a/third_party/blink/renderer/core/svg/svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_element.cc
@@ -159,9 +159,9 @@
   // Don't report any errors on attribute removal.
   if (value.IsNull())
     return;
-  GetDocument().AddConsoleMessage(
-      ConsoleMessage::Create(kRenderingMessageSource, kErrorMessageLevel,
-                             "Error: " + error.Format(tagName(), name, value)));
+  GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+      kRenderingMessageSource, mojom::ConsoleMessageLevel::kError,
+      "Error: " + error.Format(tagName(), name, value)));
 }
 
 String SVGElement::title() const {
diff --git a/third_party/blink/renderer/core/svg/svg_image_element.cc b/third_party/blink/renderer/core/svg/svg_image_element.cc
index 2556bfc..fa9224a 100644
--- a/third_party/blink/renderer/core/svg/svg_image_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_image_element.cc
@@ -182,7 +182,7 @@
             &is_default_overridden_intrinsic_size_, &message);
     if (!message.IsEmpty()) {
       GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kWarningMessageLevel, message));
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     }
 
     if (intrinsic_size_changed) {
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 5c850ab..7a08fd1 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -252,7 +252,7 @@
     PerformanceEntryVector empty_entries;
     String message = "Deprecated API for given entry type.";
     GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     return empty_entries;
   }
   return getEntriesByTypeInternal(type);
@@ -507,7 +507,7 @@
   result.alpn_negotiated_protocol = final_response.AlpnNegotiatedProtocol();
   result.connection_info = final_response.ConnectionInfoString();
   result.timing = final_response.GetResourceLoadTiming();
-  result.finish_time = info.LoadFinishTime();
+  result.response_end = info.LoadResponseEnd();
 
   result.allow_timing_details = PassesTimingAllowCheck(
       final_response, destination_origin, &context_for_use_counter);
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.cc b/third_party/blink/renderer/core/timing/performance_element_timing.cc
index 0d74d8ef..31220830 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.cc
@@ -13,17 +13,23 @@
 PerformanceElementTiming* PerformanceElementTiming::Create(
     const AtomicString& name,
     const IntRect& intersection_rect,
-    DOMHighResTimeStamp start_time) {
-  return MakeGarbageCollected<PerformanceElementTiming>(name, intersection_rect,
-                                                        start_time);
+    DOMHighResTimeStamp start_time,
+    DOMHighResTimeStamp response_end,
+    const AtomicString& identifier) {
+  return MakeGarbageCollected<PerformanceElementTiming>(
+      name, intersection_rect, start_time, response_end, identifier);
 }
 
 PerformanceElementTiming::PerformanceElementTiming(
     const AtomicString& name,
     const IntRect& intersection_rect,
-    DOMHighResTimeStamp start_time)
+    DOMHighResTimeStamp start_time,
+    DOMHighResTimeStamp response_end,
+    const AtomicString& identifier)
     : PerformanceEntry(name, start_time, start_time),
-      intersection_rect_(DOMRectReadOnly::FromIntRect(intersection_rect)) {}
+      intersection_rect_(DOMRectReadOnly::FromIntRect(intersection_rect)),
+      response_end_(response_end),
+      identifier_(identifier) {}
 
 PerformanceElementTiming::~PerformanceElementTiming() = default;
 
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.h b/third_party/blink/renderer/core/timing/performance_element_timing.h
index 35c0c913..b7653c1a 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.h
@@ -22,11 +22,15 @@
  public:
   static PerformanceElementTiming* Create(const AtomicString& name,
                                           const IntRect& intersection_rect,
-                                          DOMHighResTimeStamp start_time);
+                                          DOMHighResTimeStamp start_time,
+                                          DOMHighResTimeStamp response_end,
+                                          const AtomicString& identifier);
 
   PerformanceElementTiming(const AtomicString& name,
                            const IntRect& intersection_rect,
-                           DOMHighResTimeStamp start_time);
+                           DOMHighResTimeStamp start_time,
+                           DOMHighResTimeStamp response_end,
+                           const AtomicString& identifier);
   ~PerformanceElementTiming() override;
 
   AtomicString entryType() const override;
@@ -34,12 +38,18 @@
 
   DOMRectReadOnly* intersectionRect() const { return intersection_rect_; }
 
+  DOMHighResTimeStamp responseEnd() const { return response_end_; }
+
+  AtomicString identifier() const { return identifier_; }
+
   void Trace(blink::Visitor*) override;
 
  private:
   void BuildJSONValue(V8ObjectBuilder&) const override;
 
   Member<DOMRectReadOnly> intersection_rect_;
+  DOMHighResTimeStamp response_end_;
+  AtomicString identifier_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.idl b/third_party/blink/renderer/core/timing/performance_element_timing.idl
index ef9982f..3796be6 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.idl
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.idl
@@ -6,6 +6,8 @@
 [OriginTrialEnabled=ElementTiming]
 interface PerformanceElementTiming : PerformanceEntry {
     readonly attribute DOMRectReadOnly intersectionRect;
+    readonly attribute DOMHighResTimeStamp responseEnd;
+    readonly attribute DOMString identifier;
 
     // TODO(peria): toJSON is not in spec. https://crbug.com/736332
     [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index 9e124dc..1c400b6 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -128,7 +128,7 @@
           "its "
           "entryTypes attribute.";
       GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel, message));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
       return;
     }
     if (RuntimeEnabledFeatures::PerformanceObserverBufferedFlagEnabled() &&
@@ -137,7 +137,7 @@
           "The Performance Observer does not support buffered flag with "
           "entryTypes. ";
       GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel, message));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
     }
     filter_options_ = entry_types;
   } else {
@@ -162,7 +162,7 @@
           "The Performance Observer MUST have a valid entryType in its "
           "type attribute.";
       GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel, message));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
       return;
     }
     if (filter_options_ & entry_type) {
@@ -170,7 +170,7 @@
           "The Performance Observer has already been called with this "
           "entryType";
       GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel, message));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
       return;
     }
     if (RuntimeEnabledFeatures::PerformanceObserverBufferedFlagEnabled() &&
@@ -179,7 +179,7 @@
         String message =
             "Buffered flag does not support the long task entry type ";
         GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel, message));
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
       } else {
         // Append all entries of this type to the current performance_entries_
         // to be returned on the next callback.
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.cc b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
index bb8cb0d3..ee5bb1bd 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
@@ -53,7 +53,7 @@
                            info.allow_negative_values),
                        Performance::MonotonicTimeToDOMHighResTimeStamp(
                            time_origin,
-                           info.finish_time,
+                           info.response_end,
                            info.allow_negative_values)),
       initiator_type_(initiator_type.IsEmpty()
                           ? fetch_initiator_type_names::kOther
@@ -64,7 +64,7 @@
       time_origin_(time_origin),
       timing_(info.timing),
       last_redirect_end_time_(info.last_redirect_end_time),
-      finish_time_(info.finish_time),
+      response_end_(info.response_end),
       transfer_size_(info.transfer_size),
       encoded_body_size_(info.encoded_body_size),
       decoded_body_size_(info.decoded_body_size),
@@ -303,11 +303,11 @@
 }
 
 DOMHighResTimeStamp PerformanceResourceTiming::responseEnd() const {
-  if (finish_time_.is_null())
+  if (response_end_.is_null())
     return responseStart();
 
   return Performance::MonotonicTimeToDOMHighResTimeStamp(
-      time_origin_, finish_time_, allow_negative_value_);
+      time_origin_, response_end_, allow_negative_value_);
 }
 
 unsigned long long PerformanceResourceTiming::transferSize() const {
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.h b/third_party/blink/renderer/core/timing/performance_resource_timing.h
index 6057885..25da939 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.h
@@ -118,7 +118,7 @@
   TimeTicks time_origin_;
   scoped_refptr<ResourceLoadTiming> timing_;
   TimeTicks last_redirect_end_time_;
-  TimeTicks finish_time_;
+  TimeTicks response_end_;
   unsigned long long transfer_size_;
   unsigned long long encoded_body_size_;
   unsigned long long decoded_body_size_;
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 69931ecf..b8d9093 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -399,10 +399,13 @@
 
 void WindowPerformance::AddElementTiming(const AtomicString& name,
                                          const IntRect& rect,
-                                         TimeTicks timestamp) {
+                                         TimeTicks start_time,
+                                         TimeTicks response_end,
+                                         const AtomicString& identifier) {
   DCHECK(origin_trials::ElementTimingEnabled(GetExecutionContext()));
   PerformanceElementTiming* entry = PerformanceElementTiming::Create(
-      name, rect, MonotonicTimeToDOMHighResTimeStamp(timestamp));
+      name, rect, MonotonicTimeToDOMHighResTimeStamp(start_time),
+      MonotonicTimeToDOMHighResTimeStamp(response_end), identifier);
   if (HasObserverFor(PerformanceEntry::kElement)) {
     UseCounter::Count(GetFrame()->GetDocument(),
                       WebFeature::kElementTimingExplicitlyRequested);
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h
index d4182035..e9c85205 100644
--- a/third_party/blink/renderer/core/timing/window_performance.h
+++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -82,7 +82,9 @@
 
   void AddElementTiming(const AtomicString& name,
                         const IntRect& rect,
-                        TimeTicks timestamp);
+                        TimeTicks start_time,
+                        TimeTicks response_end,
+                        const AtomicString& identifier);
 
   void AddLayoutJankFraction(double jank_fraction);
 
diff --git a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
index 3e7d6d9..f35eb17 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
@@ -54,10 +54,11 @@
   // https://html.spec.whatwg.org/C/#runtime-script-errors-2
 }
 
-void SharedWorkerReportingProxy::ReportConsoleMessage(MessageSource,
-                                                      MessageLevel,
-                                                      const String& message,
-                                                      SourceLocation*) {
+void SharedWorkerReportingProxy::ReportConsoleMessage(
+    MessageSource,
+    mojom::ConsoleMessageLevel,
+    const String& message,
+    SourceLocation*) {
   DCHECK(!IsMainThread());
   // Not supported in SharedWorker.
 }
diff --git a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.h b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.h
index d2971f91..0b0faaa 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.h
@@ -32,7 +32,7 @@
                        std::unique_ptr<SourceLocation>,
                        int exception_id) override;
   void ReportConsoleMessage(MessageSource,
-                            MessageLevel,
+                            mojom::ConsoleMessageLevel,
                             const String& message,
                             SourceLocation*) override;
   void DidFetchScript() override;
diff --git a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc
index 553b6e59..65b8816 100644
--- a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc
+++ b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.cc
@@ -92,7 +92,7 @@
 
 void ThreadedMessagingProxyBase::ReportConsoleMessage(
     MessageSource source,
-    MessageLevel level,
+    mojom::ConsoleMessageLevel level,
     const String& message,
     std::unique_ptr<SourceLocation> location) {
   DCHECK(IsParentContextThread());
diff --git a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h
index d4b61b1..2415fc8 100644
--- a/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h
+++ b/third_party/blink/renderer/core/workers/threaded_messaging_proxy_base.h
@@ -6,9 +6,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_THREADED_MESSAGING_PROXY_BASE_H_
 
 #include "base/optional.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
-#include "third_party/blink/renderer/core/inspector/console_types.h"
 #include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
 #include "third_party/blink/renderer/core/workers/worker_backing_thread_startup_data.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
@@ -56,7 +56,7 @@
   void CountDeprecation(WebFeature);
 
   void ReportConsoleMessage(MessageSource,
-                            MessageLevel,
+                            mojom::ConsoleMessageLevel,
                             const String& message,
                             std::unique_ptr<SourceLocation>);
 
diff --git a/third_party/blink/renderer/core/workers/threaded_object_proxy_base.cc b/third_party/blink/renderer/core/workers/threaded_object_proxy_base.cc
index 1730f2ef..79d2b67 100644
--- a/third_party/blink/renderer/core/workers/threaded_object_proxy_base.cc
+++ b/third_party/blink/renderer/core/workers/threaded_object_proxy_base.cc
@@ -31,10 +31,11 @@
                       MessagingProxyWeakPtr(), feature));
 }
 
-void ThreadedObjectProxyBase::ReportConsoleMessage(MessageSource source,
-                                                   MessageLevel level,
-                                                   const String& message,
-                                                   SourceLocation* location) {
+void ThreadedObjectProxyBase::ReportConsoleMessage(
+    MessageSource source,
+    mojom::ConsoleMessageLevel level,
+    const String& message,
+    SourceLocation* location) {
   PostCrossThreadTask(
       *GetParentExecutionContextTaskRunners()->Get(TaskType::kInternalDefault),
       FROM_HERE,
diff --git a/third_party/blink/renderer/core/workers/threaded_object_proxy_base.h b/third_party/blink/renderer/core/workers/threaded_object_proxy_base.h
index b8bbe9e..3de08bc2a 100644
--- a/third_party/blink/renderer/core/workers/threaded_object_proxy_base.h
+++ b/third_party/blink/renderer/core/workers/threaded_object_proxy_base.h
@@ -32,7 +32,7 @@
   void CountFeature(WebFeature) override;
   void CountDeprecation(WebFeature) override;
   void ReportConsoleMessage(MessageSource,
-                            MessageLevel,
+                            mojom::ConsoleMessageLevel,
                             const String& message,
                             SourceLocation*) override;
   void DidCloseWorkerGlobalScope() override;
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index 59d46b70..87b5d91 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -234,9 +234,9 @@
 
   // Adds a deprecation message to the console.
   DCHECK(!Deprecation::DeprecationMessage(feature).IsEmpty());
-  AddConsoleMessage(
-      ConsoleMessage::Create(kDeprecationMessageSource, kWarningMessageLevel,
-                             Deprecation::DeprecationMessage(feature)));
+  AddConsoleMessage(ConsoleMessage::Create(
+      kDeprecationMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      Deprecation::DeprecationMessage(feature)));
   ReportingProxy().CountDeprecation(feature);
 }
 
diff --git a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
index 6362342a..2f48598 100644
--- a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
+++ b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_WORKER_REPORTING_PROXY_H_
 
 #include <memory>
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/web_feature_forward.h"
@@ -57,7 +58,7 @@
                                std::unique_ptr<SourceLocation>,
                                int exception_id) {}
   virtual void ReportConsoleMessage(MessageSource,
-                                    MessageLevel,
+                                    mojom::ConsoleMessageLevel,
                                     const String& message,
                                     SourceLocation*) {}
 
diff --git a/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc b/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
index 4474f22..66e44e3 100644
--- a/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
+++ b/third_party/blink/renderer/core/xml/parser/xml_document_parser.cc
@@ -577,8 +577,8 @@
           XMLDocumentParserScope::current_document_->Url().ElidedString() +
           ". Domains, protocols and ports must match.\n";
       XMLDocumentParserScope::current_document_->AddConsoleMessage(
-          ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel,
-                                 message));
+          ConsoleMessage::Create(kSecurityMessageSource,
+                                 mojom::ConsoleMessageLevel::kError, message));
     }
     return false;
   }
diff --git a/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc b/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
index 47e5f3c..67caaaf 100644
--- a/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
+++ b/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
@@ -65,18 +65,18 @@
   if (!console)
     return;
 
-  MessageLevel level;
+  mojom::ConsoleMessageLevel level;
   switch (error->level) {
     case XML_ERR_NONE:
-      level = kVerboseMessageLevel;
+      level = mojom::ConsoleMessageLevel::kVerbose;
       break;
     case XML_ERR_WARNING:
-      level = kWarningMessageLevel;
+      level = mojom::ConsoleMessageLevel::kWarning;
       break;
     case XML_ERR_ERROR:
     case XML_ERR_FATAL:
     default:
-      level = kErrorMessageLevel;
+      level = mojom::ConsoleMessageLevel::kError;
       break;
   }
 
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
index 9496172..5740ad6 100644
--- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
+++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request.cc
@@ -193,8 +193,8 @@
   // FIXME: It's not good to report the bad usage without indicating what source
   // line it came from.  We should pass additional parameters so we can tell the
   // console where the mistake occurred.
-  ConsoleMessage* console_message =
-      ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel, message);
+  ConsoleMessage* console_message = ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kError, message);
   context->AddConsoleMessage(console_message);
 }
 
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index 6943cd95..3e7893e 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -452,10 +452,10 @@
   // support is in.
   if (!playback_rate) {
     if (document_->GetFrame() && ExecutionContext::From(script_state)) {
-      document_->GetFrame()->Console().AddMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                                 "WorkletAnimation currently does not support "
-                                 "playback rate of Zero."));
+      document_->GetFrame()->Console().AddMessage(ConsoleMessage::Create(
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+          "WorkletAnimation currently does not support "
+          "playback rate of Zero."));
     }
     return;
   }
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
index b71004e..d25574f 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
@@ -166,7 +166,7 @@
 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && !defined(OS_MACOSX) && \
     !defined(OS_WIN)
   context->AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kInfoMessageLevel,
+      kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
       "Web Bluetooth is experimental on this platform. See "
       "https://github.com/WebBluetoothCG/web-bluetooth/blob/gh-pages/"
       "implementation-status.md"));
@@ -286,7 +286,7 @@
   // Remind developers when they are using Web Bluetooth on unsupported
   // platforms.
   context->AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kInfoMessageLevel,
+      kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
       "Web Bluetooth Scanning is experimental on this platform. See "
       "https://github.com/WebBluetoothCG/web-bluetooth/blob/gh-pages/"
       "implementation-status.md"));
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc
index 807e78e..ea66a8f4 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -246,7 +246,8 @@
                 resolver->Resolve();
                 if (message) {
                   context->AddConsoleMessage(ConsoleMessage::Create(
-                      kJSMessageSource, kWarningMessageLevel, message));
+                      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+                      message));
 
                   // If the message indicates there were duplicate requests in
                   // the batch argument list, but the operation succeeded
@@ -887,7 +888,8 @@
             }
             if (report_to_console && message) {
               context->AddConsoleMessage(ConsoleMessage::Create(
-                  kJSMessageSource, kWarningMessageLevel, message));
+                  kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+                  message));
             }
           },
           WrapPersistent(resolver), base::TimeTicks::Now(),
diff --git a/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc b/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
index 2c427ce86..8cdf0f7 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_orientation_controller.cc
@@ -180,7 +180,8 @@
       "features.md#sensor-features",
       event_name.Ascii().data());
   ConsoleMessage* console_message = ConsoleMessage::Create(
-      kJSMessageSource, kWarningMessageLevel, std::move(message));
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      std::move(message));
   frame->Console().AddMessage(console_message);
 }
 
diff --git a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
index e9475fc..4badd6d4 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
@@ -418,7 +418,8 @@
     event = CreateEncryptedEvent(WebEncryptedMediaInitDataType::kUnknown,
                                  nullptr, 0);
     media_element_->GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+        ConsoleMessage::Create(kJSMessageSource,
+                               mojom::ConsoleMessageLevel::kWarning,
                                "Media element must be CORS-same-origin with "
                                "the embedding page. If cross-origin, you "
                                "should use the `crossorigin` attribute and "
diff --git a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
index abbac260..9b1b7b8 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
@@ -295,7 +295,7 @@
     // using an empty robustness here, and provide the link to the doc in this
     // message. See http://crbug.com/720013
     resolver_->GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "It is recommended that a robustness level be specified. Not "
         "specifying the robustness level could result in unexpected "
         "behavior."));
@@ -333,9 +333,9 @@
                                   ReportOptions::kReportOnFailure)) {
     UseCounter::Count(document,
                       WebFeature::kEncryptedMediaDisabledByFeaturePolicy);
-    document->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                               kEncryptedMediaFeaturePolicyConsoleWarning));
+    document->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        kEncryptedMediaFeaturePolicyConsoleWarning));
     return ScriptPromise::RejectWithDOMException(
         script_state,
         DOMException::Create(
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.cc b/third_party/blink/renderer/modules/eventsource/event_source.cc
index 0bbeb3c..8802168 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.cc
+++ b/third_party/blink/renderer/modules/eventsource/event_source.cc
@@ -243,7 +243,8 @@
       message.Append("\") that is not UTF-8. Aborting the connection.");
       // FIXME: We are missing the source line.
       GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kErrorMessageLevel, message.ToString()));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+          message.ToString()));
     }
   } else {
     // To keep the signal-to-noise ratio low, we only log 200-response with an
@@ -256,7 +257,8 @@
           "\") that is not \"text/event-stream\". Aborting the connection.");
       // FIXME: We are missing the source line.
       GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kErrorMessageLevel, message.ToString()));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+          message.ToString()));
     }
   }
 
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 48757ee2..3cdea92b 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -262,27 +262,8 @@
 
 void WebEmbeddedWorkerImpl::AddMessageToConsole(
     const WebConsoleMessage& message) {
-  MessageLevel web_core_message_level;
-  switch (message.level) {
-    case mojom::ConsoleMessageLevel::kVerbose:
-      web_core_message_level = kVerboseMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kInfo:
-      web_core_message_level = kInfoMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kWarning:
-      web_core_message_level = kWarningMessageLevel;
-      break;
-    case mojom::ConsoleMessageLevel::kError:
-      web_core_message_level = kErrorMessageLevel;
-      break;
-    default:
-      NOTREACHED();
-      return;
-  }
-
   shadow_page_->GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-      kOtherMessageSource, web_core_message_level, message.text,
+      kOtherMessageSource, message.level, message.text,
       SourceLocation::Create(message.url, message.line_number,
                              message.column_number, nullptr)));
 }
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
index 01b79e3..67c16eb2 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
@@ -90,7 +90,7 @@
     if (options->hasAudioBitsPerSecond() || options->hasBitsPerSecond()) {
       if (audio_bps > kLargestAutoAllocatedOpusBitRate) {
         context->AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Clamping calculated audio bitrate (" + String::Number(audio_bps) +
                 "bps) to the maximum (" +
                 String::Number(kLargestAutoAllocatedOpusBitRate) + "bps)"));
@@ -99,7 +99,7 @@
 
       if (audio_bps < kSmallestPossibleOpusBitRate) {
         context->AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Clamping calculated audio bitrate (" + String::Number(audio_bps) +
                 "bps) to the minimum (" +
                 String::Number(kSmallestPossibleOpusBitRate) + "bps)"));
@@ -119,7 +119,7 @@
     if (options->hasVideoBitsPerSecond() || options->hasBitsPerSecond()) {
       if (video_bps < kSmallestPossibleVpxBitRate) {
         context->AddConsoleMessage(ConsoleMessage::Create(
-            kJSMessageSource, kWarningMessageLevel,
+            kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Clamping calculated video bitrate (" + String::Number(video_bps) +
                 "bps) to the minimum (" +
                 String::Number(kSmallestPossibleVpxBitRate) + "bps)"));
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
index 983ab58..3cf3b0f 100644
--- a/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
+++ b/third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.cc
@@ -41,7 +41,7 @@
   if (!src.ProtocolIs(url::kHttpScheme) && !src.ProtocolIs(url::kHttpsScheme) &&
       !src.ProtocolIs(url::kDataScheme) && !src.ProtocolIs(url::kBlobScheme)) {
     context->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "MediaImage src can only be of http/https/data/blob scheme: " +
             src.GetString()));
     return false;
@@ -50,7 +50,7 @@
   DCHECK(src.GetString().Is8Bit());
   if (src.GetString().length() > url::kMaxURLChars) {
     context->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "MediaImage src exceeds maximum URL length: " + src.GetString()));
     return false;
   }
@@ -76,7 +76,7 @@
     mojo_image->sizes.push_back(web_size);
     if (mojo_image->sizes.size() == kMaxNumberOfImageSizes) {
       context->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "The number of MediaImage sizes exceeds the upper limit. "
           "All remaining MediaImage will be ignored"));
       break;
@@ -107,7 +107,7 @@
       mojo_metadata->artwork.push_back(std::move(mojo_image));
     if (mojo_metadata->artwork.size() == kMaxNumberOfMediaImages) {
       context->AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "The number of MediaImage sizes exceeds the upper limit. "
           "All remaining MediaImage will be ignored"));
       break;
diff --git a/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc b/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc
index 1024e47..b637964 100644
--- a/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_constraints_impl.cc
@@ -418,7 +418,7 @@
       // TODO(crbug.com/856176): Remove the kGoogBeamforming and
       // kGoogArrayGeometry special cases.
       context->AddConsoleMessage(ConsoleMessage::Create(
-          kDeprecationMessageSource, kWarningMessageLevel,
+          kDeprecationMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "Obsolete constraint named " + String(constraint.name_) +
               " is ignored. Please stop using it."));
     } else if (constraint.name_.Equals(kVideoKind)) {
@@ -442,7 +442,7 @@
         // TODO(hta): UMA stats for unknown constraints passed.
         // https://crbug.com/576613
         context->AddConsoleMessage(ConsoleMessage::Create(
-            kDeprecationMessageSource, kWarningMessageLevel,
+            kDeprecationMessageSource, mojom::ConsoleMessageLevel::kWarning,
             "Unknown constraint named " + String(constraint.name_) +
                 " rejected"));
         // TODO(crbug.com/856176): Don't throw an error.
diff --git a/third_party/blink/renderer/modules/netinfo/network_information.cc b/third_party/blink/renderer/modules/netinfo/network_information.cc
index a7f95a2..da587a1d 100644
--- a/third_party/blink/renderer/modules/netinfo/network_information.cc
+++ b/third_party/blink/renderer/modules/netinfo/network_information.cc
@@ -316,9 +316,9 @@
   web_holdback_console_message_shown_ = true;
   if (!GetNetworkStateNotifier().GetWebHoldbackEffectiveType())
     return;
-  GetExecutionContext()->AddConsoleMessage(
-      ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
-                             GetConsoleLogStringForWebHoldback()));
+  GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+      kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      GetConsoleLogStringForWebHoldback()));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/payment_handler_utils.cc b/third_party/blink/renderer/modules/payments/payment_handler_utils.cc
index 215faa2..ef536d1 100644
--- a/third_party/blink/renderer/modules/payments/payment_handler_utils.cc
+++ b/third_party/blink/renderer/modules/payments/payment_handler_utils.cc
@@ -52,7 +52,7 @@
 
   DCHECK(execution_context);
   execution_context->AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kWarningMessageLevel, error_message));
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, error_message));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index a5f100b..b3e57a6d7 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -251,7 +251,7 @@
 
   if (item->label().IsEmpty()) {
     execution_context.AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kErrorMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
         "Empty " + item_name + " label may be confusing the user"));
     return;
   }
@@ -315,7 +315,7 @@
 
     if (option->id().IsEmpty()) {
       execution_context.AddConsoleMessage(ConsoleMessage::Create(
-          kJSMessageSource, kWarningMessageLevel,
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "Empty shipping option ID may be hard to debug"));
       return;
     }
@@ -757,8 +757,8 @@
       "reject the promise, but allowing continued usage on localhost and "
       "file:// scheme origins.",
       method_name);
-  execution_context.AddConsoleMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, error));
+  execution_context.AddConsoleMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, error));
 }
 
 }  // namespace
@@ -939,33 +939,33 @@
 
   if (!options_->requestPayerName() && errors->hasPayer() &&
       errors->payer()->hasName()) {
-    GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                               "The payer.name passed to retry() may not be "
-                               "shown because requestPayerName is false"));
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "The payer.name passed to retry() may not be "
+        "shown because requestPayerName is false"));
   }
 
   if (!options_->requestPayerEmail() && errors->hasPayer() &&
       errors->payer()->hasEmail()) {
-    GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                               "The payer.email passed to retry() may not be "
-                               "shown because requestPayerEmail is false"));
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "The payer.email passed to retry() may not be "
+        "shown because requestPayerEmail is false"));
   }
 
   if (!options_->requestPayerPhone() && errors->hasPayer() &&
       errors->payer()->hasPhone()) {
-    GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                               "The payer.phone passed to retry() may not be "
-                               "shown because requestPayerPhone is false"));
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "The payer.phone passed to retry() may not be "
+        "shown because requestPayerPhone is false"));
   }
 
   if (!options_->requestShipping() && errors->hasShippingAddress()) {
-    GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                               "The shippingAddress passed to retry() may not "
-                               "be shown because requestShipping is false"));
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "The shippingAddress passed to retry() may not "
+        "be shown because requestShipping is false"));
   }
 
   complete_timer_.Stop();
@@ -1189,7 +1189,7 @@
   DispatchEvent(*event);
   if (!event->is_waiting_for_update()) {
     GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "No updateWith() call in 'shippingaddresschange' event handler. User "
         "may see outdated line items and total."));
     payment_provider_->NoUpdatedPaymentDetails();
@@ -1208,7 +1208,7 @@
   DispatchEvent(*event);
   if (!event->is_waiting_for_update()) {
     GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "No updateWith() call in 'shippingoptionchange' event handler. User "
         "may see outdated line items and total."));
     payment_provider_->NoUpdatedPaymentDetails();
@@ -1468,15 +1468,15 @@
 }
 
 void PaymentRequest::WarnNoFavicon() {
-  GetExecutionContext()->AddConsoleMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                             "Favicon not found for PaymentRequest UI. User "
-                             "may not recognize the website."));
+  GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      "Favicon not found for PaymentRequest UI. User "
+      "may not recognize the website."));
 }
 
 void PaymentRequest::OnCompleteTimeout(TimerBase*) {
   GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-      kJSMessageSource, kErrorMessageLevel,
+      kJSMessageSource, mojom::ConsoleMessageLevel::kError,
       "Timed out waiting for a PaymentResponse.complete() call."));
   payment_provider_->Complete(payments::mojom::blink::PaymentComplete(kFail));
   ClearResolversAndCloseMojoConnection();
diff --git a/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc b/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc
index cfdcd13..9837c74 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_respond_with_observer.cc
@@ -56,11 +56,11 @@
 
   // Check payment response validity.
   if (!response->hasMethodName() || !response->hasDetails()) {
-    GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel,
-                               "'PaymentHandlerResponse.methodName' and "
-                               "'PaymentHandlerResponse.details' must not "
-                               "be empty in payment response."));
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
+        "'PaymentHandlerResponse.methodName' and "
+        "'PaymentHandlerResponse.details' must not "
+        "be empty in payment response."));
     OnResponseRejected(mojom::ServiceWorkerResponseError::kUnknown);
     return;
   }
@@ -73,7 +73,7 @@
                            response->details().V8Value().As<v8::Object>())
            .ToLocal(&details_value)) {
     GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kErrorMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError,
         "Failed to stringify PaymentHandlerResponse.details in payment "
         "response."));
     OnResponseRejected(mojom::ServiceWorkerResponseError::kUnknown);
diff --git a/third_party/blink/renderer/modules/sensor/sensor.cc b/third_party/blink/renderer/modules/sensor/sensor.cc
index 74a088e..8d017828 100644
--- a/third_party/blink/renderer/modules/sensor/sensor.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor.cc
@@ -65,7 +65,8 @@
           "Maximum allowed frequency value for this sensor type is %.0f Hz.",
           max_allowed_frequency);
       ConsoleMessage* console_message = ConsoleMessage::Create(
-          kJSMessageSource, kInfoMessageLevel, std::move(message));
+          kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+          std::move(message));
       execution_context->AddConsoleMessage(console_message);
     }
   }
diff --git a/third_party/blink/renderer/modules/sensor/sensor_inspector_agent.cc b/third_party/blink/renderer/modules/sensor/sensor_inspector_agent.cc
index 296d243..70d1b3f 100644
--- a/third_party/blink/renderer/modules/sensor/sensor_inspector_agent.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor_inspector_agent.cc
@@ -79,7 +79,8 @@
     Document* document = provider_->GetSupplementable();
     if (document) {
       ConsoleMessage* console_message = ConsoleMessage::Create(
-          kJSMessageSource, kInfoMessageLevel, kInspectorConsoleMessage);
+          kJSMessageSource, mojom::ConsoleMessageLevel::kInfo,
+          kInspectorConsoleMessage);
       document->AddConsoleMessage(console_message);
     }
     provider_->set_inspector_mode(true);
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_event.cc b/third_party/blink/renderer/modules/service_worker/fetch_event.cc
index 6d5484ff..93034c5 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_event.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_event.cc
@@ -202,7 +202,7 @@
   scoped_refptr<ResourceTimingInfo> info =
       ResourceTimingInfo::Create("navigation", request_time);
   info->SetNegativeAllowed(true);
-  info->SetLoadFinishTime(completion_time);
+  info->SetLoadResponseEnd(completion_time);
   info->SetInitialURL(request_->url());
   info->SetFinalResponse(resource_response);
   info->AddFinalTransferSize(encoded_data_length);
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
index ce7d687..d8ea0a16 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_respond_with_observer.cc
@@ -216,9 +216,9 @@
 void FetchRespondWithObserver::OnResponseRejected(
     ServiceWorkerResponseError error) {
   DCHECK(GetExecutionContext());
-  GetExecutionContext()->AddConsoleMessage(
-      ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                             GetMessageForResponseError(error, request_url_)));
+  GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+      kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+      GetMessageForResponseError(error, request_url_)));
 
   // The default value of WebServiceWorkerResponse's status is 0, which maps
   // to a network error.
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index e09cb9b..f7e2c95 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -291,8 +291,8 @@
         "Event handler of '%s' event must be added on the initial evaluation "
         "of worker script.",
         event_type.Utf8().data());
-    AddConsoleMessage(ConsoleMessage::Create(kJSMessageSource,
-                                             kWarningMessageLevel, message));
+    AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
   }
   return WorkerGlobalScope::AddEventListenerInternal(event_type, listener,
                                                      options);
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index 300a4a1..69f8f1e 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -434,7 +434,7 @@
                                        : error->unsanitized_message;
   if (!error_message.IsEmpty()) {
     WorkerGlobalScope()->AddConsoleMessage(ConsoleMessage::Create(
-        kWorkerMessageSource, blink::MessageLevel::kErrorMessageLevel,
+        kWorkerMessageSource, mojom::ConsoleMessageLevel::kError,
         error_message));
   }
   // Reject the preloadResponse promise.
@@ -614,32 +614,12 @@
 
 void ServiceWorkerGlobalScopeProxy::ReportConsoleMessage(
     MessageSource source,
-    MessageLevel level,
+    mojom::ConsoleMessageLevel level,
     const String& message,
     SourceLocation* location) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
-
-  // TODO(https://crbug.com/937184): This back-and-forth conversion will be
-  // unnecessary once we converge on blink::mojom::ConsoleMessageLevel.
-  blink::mojom::ConsoleMessageLevel console_message_level =
-      blink::mojom::ConsoleMessageLevel::kInfo;
-  switch (level) {
-    case kVerboseMessageLevel:
-      console_message_level = blink::mojom::ConsoleMessageLevel::kVerbose;
-      break;
-    case kInfoMessageLevel:
-      console_message_level = blink::mojom::ConsoleMessageLevel::kInfo;
-      break;
-    case kWarningMessageLevel:
-      console_message_level = blink::mojom::ConsoleMessageLevel::kWarning;
-      break;
-    case kErrorMessageLevel:
-      console_message_level = blink::mojom::ConsoleMessageLevel::kError;
-      break;
-  }
-
-  Client().ReportConsoleMessage(source, console_message_level, message,
-                                location->LineNumber(), location->Url());
+  Client().ReportConsoleMessage(source, level, message, location->LineNumber(),
+                                location->Url());
 }
 
 void ServiceWorkerGlobalScopeProxy::WillInitializeWorkerContext() {
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
index d258eb4..f841f00 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
@@ -152,7 +152,7 @@
                        std::unique_ptr<SourceLocation>,
                        int exception_id) override;
   void ReportConsoleMessage(MessageSource,
-                            MessageLevel,
+                            mojom::ConsoleMessageLevel,
                             const String& message,
                             SourceLocation*) override;
   void WillInitializeWorkerContext() override;
diff --git a/third_party/blink/renderer/modules/vr/navigator_vr.cc b/third_party/blink/renderer/modules/vr/navigator_vr.cc
index fda8db78..489678d 100644
--- a/third_party/blink/renderer/modules/vr/navigator_vr.cc
+++ b/third_party/blink/renderer/modules/vr/navigator_vr.cc
@@ -85,7 +85,7 @@
     if (controller_) {
       if (frame->GetDocument()) {
         frame->GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-            kOtherMessageSource, kErrorMessageLevel,
+            kOtherMessageSource, mojom::ConsoleMessageLevel::kError,
             "Cannot use navigator.xr if the legacy VR API is already in use."));
       }
       return nullptr;
diff --git a/third_party/blink/renderer/modules/vr/vr_display.cc b/third_party/blink/renderer/modules/vr/vr_display.cc
index a391d79..1bbfb4a 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.cc
+++ b/third_party/blink/renderer/modules/vr/vr_display.cc
@@ -221,10 +221,10 @@
   if (!in_animation_frame_) {
     Document* doc = navigator_vr_->GetDocument();
     if (doc) {
-      doc->AddConsoleMessage(
-          ConsoleMessage::Create(kRenderingMessageSource, kWarningMessageLevel,
-                                 "getFrameData must be called within a "
-                                 "VRDisplay.requestAnimationFrame callback."));
+      doc->AddConsoleMessage(ConsoleMessage::Create(
+          kRenderingMessageSource, mojom::ConsoleMessageLevel::kWarning,
+          "getFrameData must be called within a "
+          "VRDisplay.requestAnimationFrame callback."));
     }
     return false;
   }
@@ -773,16 +773,16 @@
 
   if (!is_presenting_) {
     doc->AddConsoleMessage(ConsoleMessage::Create(
-        kRenderingMessageSource, kWarningMessageLevel,
+        kRenderingMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "submitFrame has no effect when the VRDisplay is not presenting."));
     return;
   }
 
   if (!in_animation_frame_) {
-    doc->AddConsoleMessage(
-        ConsoleMessage::Create(kRenderingMessageSource, kWarningMessageLevel,
-                               "submitFrame must be called within a "
-                               "VRDisplay.requestAnimationFrame callback."));
+    doc->AddConsoleMessage(ConsoleMessage::Create(
+        kRenderingMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "submitFrame must be called within a "
+        "VRDisplay.requestAnimationFrame callback."));
     return;
   }
 
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 75c3b2ab..53c7875 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -440,13 +440,13 @@
       DCHECK(document->GetFrame() &&
              document->GetFrame()->IsCrossOriginSubframe());
       document->AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kWarningMessageLevel,
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "The AudioContext was not allowed to start. It must be resumed (or "
           "created) from a user gesture event handler. https://goo.gl/7K7WLu"));
       break;
     case AutoplayPolicy::Type::kDocumentUserActivationRequired:
       document->AddConsoleMessage(ConsoleMessage::Create(
-          kOtherMessageSource, kWarningMessageLevel,
+          kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
           "The AudioContext was not allowed to start. It must be resumed (or "
           "created) after a user gesture on the page. https://goo.gl/7K7WLu"));
       break;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.cc b/third_party/blink/renderer/modules/webaudio/audio_param.cc
index c70ddc2..42e36775 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.cc
@@ -375,7 +375,7 @@
 void AudioParam::WarnIfOutsideRange(const String& param_method, float value) {
   if (value < minValue() || value > maxValue()) {
     Context()->GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel,
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
         Handler().GetParamName() + "." + param_method + " " +
             String::Number(value) + " outside nominal range [" +
             String::Number(minValue()) + ", " + String::Number(maxValue()) +
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
index 05393cf..f0c03ece 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -211,17 +211,17 @@
   DCHECK(handler);
 
   if (IsContextClosed() && GetDocument()) {
-    GetDocument()->AddConsoleMessage(
-        ConsoleMessage::Create(kOtherMessageSource, kWarningMessageLevel,
-                               "Construction of " + handler->NodeTypeName() +
-                                   " is not useful when context is closed."));
+    GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        "Construction of " + handler->NodeTypeName() +
+            " is not useful when context is closed."));
   }
 }
 
 void BaseAudioContext::WarnForConnectionIfContextClosed() const {
   if (IsContextClosed() && GetDocument()) {
     GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Connecting nodes after the context has been closed is not useful."));
   }
 }
diff --git a/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc b/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
index 93a7970..6a9a8dd 100644
--- a/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/iir_filter_node.cc
@@ -166,7 +166,8 @@
     message.Append(']');
 
     context.GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message.ToString()));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+        message.ToString()));
   }
 
   return MakeGarbageCollected<IIRFilterNode>(context, feedforward_coef,
diff --git a/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.cc b/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.cc
index 6970b57..8438502 100644
--- a/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/media_element_audio_source_node.cc
@@ -142,11 +142,11 @@
 
 void MediaElementAudioSourceHandler::PrintCorsMessage(const String& message) {
   if (Context()->GetExecutionContext()) {
-    Context()->GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kSecurityMessageSource, kInfoMessageLevel,
-                               "MediaElementAudioSource outputs zeroes due to "
-                               "CORS access restrictions for " +
-                                   message));
+    Context()->GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kSecurityMessageSource, mojom::ConsoleMessageLevel::kInfo,
+        "MediaElementAudioSource outputs zeroes due to "
+        "CORS access restrictions for " +
+            message));
   }
 }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/database.cc b/third_party/blink/renderer/modules/webdatabase/database.cc
index bd70c42f..b5a1235 100644
--- a/third_party/blink/renderer/modules/webdatabase/database.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database.cc
@@ -764,7 +764,7 @@
 
 void Database::LogErrorMessage(const String& message) {
   GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-      kStorageMessageSource, kErrorMessageLevel, message));
+      kStorageMessageSource, mojom::ConsoleMessageLevel::kError, message));
 }
 
 ExecutionContext* Database::GetExecutionContext() const {
diff --git a/third_party/blink/renderer/modules/webdatabase/database_manager.cc b/third_party/blink/renderer/modules/webdatabase/database_manager.cc
index eafeea1..9dda0be 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_manager.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_manager.cc
@@ -200,7 +200,7 @@
 void DatabaseManager::LogErrorMessage(ExecutionContext* context,
                                       const String& message) {
   context->AddConsoleMessage(ConsoleMessage::Create(
-      kStorageMessageSource, kErrorMessageLevel, message));
+      kStorageMessageSource, mojom::ConsoleMessageLevel::kError, message));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 3d63dd3..780aab9 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -7384,7 +7384,7 @@
   if (!canvas())
     return;
   canvas()->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
-      kRenderingMessageSource, kWarningMessageLevel, message));
+      kRenderingMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
 }
 
 bool WebGLRenderingContextBase::ValidateFramebufferFuncParameters(
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.cc b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
index c0f9402..89e5629d 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
@@ -254,8 +254,8 @@
 
 void DOMWebSocket::LogError(const String& message) {
   if (GetExecutionContext()) {
-    GetExecutionContext()->AddConsoleMessage(
-        ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel, message));
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        kJSMessageSource, mojom::ConsoleMessageLevel::kError, message));
   }
 }
 
@@ -613,7 +613,7 @@
   if (state_ == kConnecting) {
     state_ = kClosing;
     channel_->Fail("WebSocket is closed before the connection is established.",
-                   kWarningMessageLevel,
+                   mojom::ConsoleMessageLevel::kWarning,
                    SourceLocation::Create(String(), 0, 0, nullptr));
     return;
   }
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc b/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc
index 430aadc..1771832 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket_test.cc
@@ -66,9 +66,12 @@
   }
   MOCK_CONST_METHOD0(BufferedAmount, unsigned());
   MOCK_METHOD2(Close, void(int, const String&));
-  MOCK_METHOD3(FailMock, void(const String&, MessageLevel, SourceLocation*));
+  MOCK_METHOD3(FailMock,
+               void(const String&,
+                    mojom::ConsoleMessageLevel,
+                    SourceLocation*));
   void Fail(const String& reason,
-            MessageLevel level,
+            mojom::ConsoleMessageLevel level,
             std::unique_ptr<SourceLocation> location) override {
     FailMock(reason, level, location.get());
   }
@@ -485,7 +488,7 @@
         websocket_scope.Channel(),
         FailMock(
             String("WebSocket is closed before the connection is established."),
-            kWarningMessageLevel, _));
+            mojom::ConsoleMessageLevel::kWarning, _));
   }
   websocket_scope.Socket().Connect("ws://example.com/", Vector<String>(),
                                    scope.GetExceptionState());
diff --git a/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc b/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
index 8f832b8..4eb1db5 100644
--- a/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/web_pepper_socket_impl.cc
@@ -122,7 +122,7 @@
 }
 
 void WebPepperSocketImpl::Fail(const WebString& reason) {
-  private_->Fail(reason, kErrorMessageLevel,
+  private_->Fail(reason, mojom::ConsoleMessageLevel::kError,
                  SourceLocation::Create(String(), 0, 0, nullptr));
 }
 
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel.h b/third_party/blink/renderer/modules/websockets/websocket_channel.h
index aa300aa4..31c006a7 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel.h
@@ -33,8 +33,8 @@
 
 #include <memory>
 #include "base/macros.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
-#include "third_party/blink/renderer/core/inspector/console_types.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -82,15 +82,14 @@
   virtual void Close(int code, const String& reason) = 0;
 
   // Log the reason text and close the connection. Will call DidClose().
-  // The MessageLevel parameter will be used for the level of the message
-  // shown at the devtools console.
-  // SourceLocation parameter may be shown with the reason text
-  // at the devtools console. Even if location is specified, it may be ignored
-  // and the "current" location in the sense of JavaScript execution
-  // may be shown if this method is called in a JS execution context.
-  // Location should not be null.
+  // The mojom::ConsoleMessageLevel parameter will be used for the level
+  // of the message shown at the devtools console. SourceLocation parameter may
+  // be shown with the reason text at the devtools console. Even if location is
+  // specified, it may be ignored and the "current" location in the sense of
+  // JavaScript execution may be shown if this method is called in a JS
+  // execution context. Location should not be null.
   virtual void Fail(const String& reason,
-                    MessageLevel,
+                    mojom::ConsoleMessageLevel,
                     std::unique_ptr<SourceLocation>) = 0;
 
   // Do not call any methods after calling this method.
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
index 2469f220..fc2b705e 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
@@ -228,7 +228,7 @@
         "Connecting to a non-secure WebSocket server from a secure origin is "
         "deprecated.";
     execution_context_->AddConsoleMessage(ConsoleMessage::Create(
-        kJSMessageSource, kWarningMessageLevel, message));
+        kJSMessageSource, mojom::ConsoleMessageLevel::kWarning, message));
   }
 
   url_ = url;
@@ -362,7 +362,7 @@
 }
 
 void WebSocketChannelImpl::Fail(const String& reason,
-                                MessageLevel level,
+                                mojom::ConsoleMessageLevel level,
                                 std::unique_ptr<SourceLocation> location) {
   NETWORK_DVLOG(1) << this << " Fail(" << reason << ")";
   probe::DidReceiveWebSocketMessageError(execution_context_, identifier_,
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
index dd367d46..402fa3a0 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
@@ -106,7 +106,7 @@
   // argument to omit payload.
   void Close(int code, const String& reason) override;
   void Fail(const String& reason,
-            MessageLevel,
+            mojom::ConsoleMessageLevel,
             std::unique_ptr<SourceLocation>) override;
   void Disconnect() override;
 
@@ -141,7 +141,8 @@
   void FlowControlIfNecessary();
   void InitialFlowControl();
   void FailAsError(const String& reason) {
-    Fail(reason, kErrorMessageLevel, location_at_construction_->Clone());
+    Fail(reason, mojom::ConsoleMessageLevel::kError,
+         location_at_construction_->Clone());
   }
   void AbortAsyncOperations();
   void HandleDidClose(bool was_clean, uint16_t code, const String& reason);
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
index eafd07f0..75362aa8 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
@@ -799,7 +799,8 @@
                  WebSocketChannel::kCloseEventCodeAbnormalClosure, String()));
   }
 
-  Channel()->Fail("fail message from WebSocket", kErrorMessageLevel,
+  Channel()->Fail("fail message from WebSocket",
+                  mojom::ConsoleMessageLevel::kError,
                   SourceLocation::Create(String(), 0, 0, nullptr));
 }
 
@@ -876,7 +877,8 @@
     EXPECT_CALL(checkpoint, Call(1));
   }
   Channel()->Connect(url(), "");
-  Channel()->Fail("close during handshake", kWarningMessageLevel,
+  Channel()->Fail("close during handshake",
+                  mojom::ConsoleMessageLevel::kWarning,
                   SourceLocation::Create(String(), 0, 0, nullptr));
   checkpoint.Call(1);
 }
@@ -896,7 +898,8 @@
   }
   Channel()->Connect(url(), "");
   HandleClient()->DidConnect(Handle(), String("a"), String("b"));
-  Channel()->Fail("close during handshake", kWarningMessageLevel,
+  Channel()->Fail("close during handshake",
+                  mojom::ConsoleMessageLevel::kWarning,
                   SourceLocation::Create(String(), 0, 0, nullptr));
   checkpoint.Call(1);
 }
diff --git a/third_party/blink/renderer/modules/webusb/usb_device.cc b/third_party/blink/renderer/modules/webusb/usb_device.cc
index 091e727..c8aad9d 100644
--- a/third_party/blink/renderer/modules/webusb/usb_device.cc
+++ b/third_party/blink/renderer/modules/webusb/usb_device.cc
@@ -239,11 +239,11 @@
     } else if (claimed_interfaces_.Get(interface_index)) {
       resolver->Resolve();
     } else if (IsProtectedInterfaceClass(interface_index)) {
-      GetExecutionContext()->AddConsoleMessage(
-          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
-                                 "An attempt to claim a USB device interface "
-                                 "has been blocked because it "
-                                 "implements a protected interface class."));
+      GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+          kJSMessageSource, mojom::ConsoleMessageLevel::kWarning,
+          "An attempt to claim a USB device interface "
+          "has been blocked because it "
+          "implements a protected interface class."));
       resolver->Reject(DOMException::Create(
           DOMExceptionCode::kSecurityError,
           "The requested interface implements a protected class."));
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index 39b7e9e..ef7a4f44 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -239,7 +239,7 @@
     }
 
     doc->AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
+        kOtherMessageSource, mojom::ConsoleMessageLevel::kWarning,
         "Inline AR is deprecated and will be removed soon."));
   }
 
diff --git a/third_party/blink/renderer/platform/heap/persistent_node.h b/third_party/blink/renderer/platform/heap/persistent_node.h
index 31c31cd6f..51df614 100644
--- a/third_party/blink/renderer/platform/heap/persistent_node.h
+++ b/third_party/blink/renderer/platform/heap/persistent_node.h
@@ -219,7 +219,7 @@
 };
 
 // Protected by ProcessHeap::CrossThreadPersistentMutex.
-class CrossThreadPersistentRegion final {
+class PLATFORM_EXPORT CrossThreadPersistentRegion final {
   USING_FAST_MALLOC(CrossThreadPersistentRegion);
 
  public:
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index 1f33f1bb..b28da80 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -338,10 +338,10 @@
   }
 }
 
-void Resource::Finish(TimeTicks load_finish_time,
+void Resource::Finish(TimeTicks load_response_end,
                       base::SingleThreadTaskRunner* task_runner) {
   DCHECK(!is_revalidating_);
-  load_finish_time_ = load_finish_time;
+  load_response_end_ = load_response_end;
   if (!ErrorOccurred())
     status_ = ResourceStatus::kCached;
   loader_ = nullptr;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index 4fd6b2d1..3a641467 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -347,7 +347,7 @@
   virtual void DidDownloadData(unsigned long long) {}
   virtual void DidDownloadToBlob(scoped_refptr<BlobDataHandle>) {}
 
-  TimeTicks LoadFinishTime() const { return load_finish_time_; }
+  TimeTicks LoadResponseEnd() const { return load_response_end_; }
 
   void SetEncodedDataLength(int64_t value) {
     response_.SetEncodedDataLength(value);
@@ -531,7 +531,7 @@
 
   base::Optional<ResourceError> error_;
 
-  TimeTicks load_finish_time_;
+  TimeTicks load_response_end_;
 
   unsigned long identifier_;
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 8275a42..c4e42d31 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -574,7 +574,7 @@
     ResourceResponse final_response = resource->GetResponse();
     final_response.SetResourceLoadTiming(nullptr);
     info->SetFinalResponse(final_response);
-    info->SetLoadFinishTime(info->InitialTime());
+    info->SetLoadResponseEnd(info->InitialTime());
     scheduled_resource_timing_reports_.push_back(std::move(info));
     if (!resource_timing_report_timer_.IsActive())
       resource_timing_report_timer_.StartOneShot(TimeDelta(), FROM_HERE);
@@ -1711,7 +1711,7 @@
 
 void ResourceFetcher::HandleLoaderFinish(
     Resource* resource,
-    TimeTicks finish_time,
+    TimeTicks response_end,
     LoaderFinishType type,
     uint32_t inflight_keepalive_bytes,
     bool should_report_corb_blocking,
@@ -1747,7 +1747,7 @@
                               : resource->GetResourceRequest()
                                     .GetInitialUrlForResourceTiming());
       info->SetFinalResponse(resource->GetResponse());
-      info->SetLoadFinishTime(finish_time);
+      info->SetLoadResponseEnd(response_end);
       // encodedDataLength == -1 means "not available".
       // TODO(ricea): Find cases where it is not available but the
       // PerformanceResourceTiming spec requires it to be available and fix
@@ -1768,7 +1768,7 @@
           ResourceTimingInfo::Create(info->InitiatorType(),
                                      timing_info.start_time);
       preflight_info->SetInitialURL(info->InitialURL());
-      preflight_info->SetLoadFinishTime(timing_info.finish_time);
+      preflight_info->SetLoadResponseEnd(timing_info.response_end);
       preflight_info->AddFinalTransferSize(timing_info.transfer_size);
 
       // Set a provisional response to provide possible other information.
@@ -1788,7 +1788,7 @@
 
   resource->VirtualTimePauser().UnpauseVirtualTime();
   if (type == kDidFinishLoading) {
-    resource->Finish(finish_time, task_runner_.get());
+    resource->Finish(response_end, task_runner_.get());
 
     // Since this resource came from the network stack we only schedule a stale
     // while revalidate request if the network asked us to. If we called
@@ -1818,7 +1818,7 @@
     }
   }
   Context().DispatchDidFinishLoading(
-      resource->Identifier(), finish_time, encoded_data_length,
+      resource->Identifier(), response_end, encoded_data_length,
       resource->GetResponse().DecodedBodyLength(), should_report_corb_blocking,
       FetchContext::ResourceResponseType::kNotFromMemoryCache);
   resource->ReloadIfLoFiOrPlaceholderImage(this, Resource::kReloadIfNeeded);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
index 0674429a..0d97155 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/aggregated_metric_reporter.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_status.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
 
 namespace blink {
 
@@ -421,7 +422,7 @@
   DCHECK(ThrottleOption::kStoppable == option ||
          ThrottleOption::kThrottleable == option);
   if (pending_requests_[option].empty())
-    pending_queue_update_times_[option] = base::TimeTicks::Now();
+    pending_queue_update_times_[option] = CurrentTime();
   pending_requests_[option].insert(request_info);
   pending_request_map_.insert(
       *id, MakeGarbageCollected<ClientInfo>(client, option, priority,
@@ -656,14 +657,12 @@
   if (use_stoppable) {
     *id = stoppable_it->client_id;
     stoppable_queue.erase(stoppable_it);
-    pending_queue_update_times_[ThrottleOption::kStoppable] =
-        base::TimeTicks::Now();
+    pending_queue_update_times_[ThrottleOption::kStoppable] = CurrentTime();
     return true;
   }
   *id = throttleable_it->client_id;
   throttleable_queue.erase(throttleable_it);
-  pending_queue_update_times_[ThrottleOption::kThrottleable] =
-      base::TimeTicks::Now();
+  pending_queue_update_times_[ThrottleOption::kThrottleable] = CurrentTime();
   return true;
 }
 
@@ -727,8 +726,7 @@
   if (is_console_info_shown_ || pending_request_map_.IsEmpty())
     return;
 
-  const base::TimeTicks limit =
-      base::TimeTicks::Now() - base::TimeDelta::FromMinutes(1);
+  const double limit = CurrentTime() - 60;  // In seconds
   ThrottleOption target_option;
   if (pending_queue_update_times_[ThrottleOption::kThrottleable] < limit &&
       !IsPendingRequestEffectivelyEmpty(ThrottleOption::kThrottleable)) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
index ed4e985..050ef933 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
@@ -331,8 +331,9 @@
            std::set<ClientIdWithPriority, ClientIdWithPriority::Compare>>
       pending_requests_;
 
-  // Remembers times when the top request in each queue is processed.
-  std::map<ThrottleOption, base::TimeTicks> pending_queue_update_times_;
+  // Remembers elapsed times in seconds when the top request in each queue is
+  // processed.
+  std::map<ThrottleOption, double> pending_queue_update_times_;
 
   // Holds an internal class instance to monitor and report traffic.
   std::unique_ptr<TrafficMonitor> traffic_monitor_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
index 8db7407..88e7858 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/wtf/scoped_mock_clock.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
@@ -44,14 +45,17 @@
   void SetDelegate(MockClientDelegate* delegate) { delegate_ = delegate; }
 
   void Run() override {
-    if (delegate_) {
+    if (delegate_)
       delegate_->NotifyRun(this);
-    }
     EXPECT_FALSE(was_run_);
     was_run_ = true;
   }
-  ConsoleLogger* GetConsoleLogger() override { return console_logger_; }
   bool WasRun() { return was_run_; }
+  ConsoleLogger* GetConsoleLogger() override {
+    was_console_logger_obtained_ = true;
+    return console_logger_;
+  }
+  bool WasConsoleLoggerObtained() { return was_console_logger_obtained_; }
 
   void Trace(blink::Visitor* visitor) override {
     ResourceLoadSchedulerClient::Trace(visitor);
@@ -63,6 +67,7 @@
       MakeGarbageCollected<NullConsoleLogger>();
   MockClientDelegate* delegate_;
   bool was_run_ = false;
+  bool was_console_logger_obtained_ = false;
 };
 
 class ResourceLoadSchedulerTest : public testing::Test {
@@ -619,5 +624,51 @@
   EXPECT_TRUE(Release(id1));
 }
 
+TEST_F(ResourceLoadSchedulerTest, ConsoleMessage) {
+  WTF::ScopedMockClock mock_clock;
+  Scheduler()->SetOutstandingLimitForTesting(0, 0);
+  Scheduler()->OnLifecycleStateChanged(
+      scheduler::SchedulingLifecycleState::kThrottled);
+
+  // Push two requests into the queue.
+  MockClient* client1 = MakeGarbageCollected<MockClient>();
+  ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+  Scheduler()->Request(client1, ThrottleOption::kThrottleable,
+                       ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+                       &id1);
+  EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+  EXPECT_FALSE(client1->WasRun());
+
+  MockClient* client2 = MakeGarbageCollected<MockClient>();
+  ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+  Scheduler()->Request(client2, ThrottleOption::kThrottleable,
+                       ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+                       &id2);
+  EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+  EXPECT_FALSE(client2->WasRun());
+
+  // Cancel the first request
+  EXPECT_TRUE(Release(id1));
+
+  // Advance current time a little and triggers an life cycle event, but it
+  // still won't awake the warning logic.
+  mock_clock.Advance(WTF::TimeDelta::FromSeconds(50));
+  Scheduler()->OnLifecycleStateChanged(
+      scheduler::SchedulingLifecycleState::kNotThrottled);
+  EXPECT_FALSE(client1->WasConsoleLoggerObtained());
+  EXPECT_FALSE(client2->WasConsoleLoggerObtained());
+  Scheduler()->OnLifecycleStateChanged(
+      scheduler::SchedulingLifecycleState::kThrottled);
+
+  // Modify current time to awake the console warning logic, and the second
+  // client should be used for console logging.
+  mock_clock.Advance(WTF::TimeDelta::FromSeconds(15));
+  Scheduler()->OnLifecycleStateChanged(
+      scheduler::SchedulingLifecycleState::kNotThrottled);
+  EXPECT_FALSE(client1->WasConsoleLoggerObtained());
+  EXPECT_TRUE(client2->WasConsoleLoggerObtained());
+  EXPECT_TRUE(Release(id2));
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index d015a32..5de5fbf6 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -499,7 +499,7 @@
 
   const ResourceResponse& response = resource_->GetResponse();
   if (deferred_finish_loading_info_) {
-    DidFinishLoading(deferred_finish_loading_info_->finish_time,
+    DidFinishLoading(deferred_finish_loading_info_->response_end,
                      response.EncodedDataLength(), response.EncodedBodyLength(),
                      response.DecodedBodyLength(),
                      deferred_finish_loading_info_->should_report_corb_blocking,
@@ -1114,7 +1114,7 @@
 }
 
 void ResourceLoader::DidFinishLoading(
-    TimeTicks finish_time,
+    TimeTicks response_end,
     int64_t encoded_data_length,
     int64_t encoded_body_length,
     int64_t decoded_body_length,
@@ -1131,7 +1131,7 @@
   if ((response_body_loader_ && !has_seen_end_of_body_) ||
       (is_downloading_to_blob_ && !blob_finished_ && blob_response_started_)) {
     deferred_finish_loading_info_ = DeferredFinishLoadingInfo{
-        finish_time, should_report_corb_blocking, cors_preflight_timing_info};
+        response_end, should_report_corb_blocking, cors_preflight_timing_info};
     return;
   }
 
@@ -1151,7 +1151,7 @@
       "endData", EndResourceLoadData(RequestOutcome::kSuccess));
 
   fetcher_->HandleLoaderFinish(
-      resource_.Get(), finish_time, ResourceFetcher::kDidFinishLoading,
+      resource_.Get(), response_end, ResourceFetcher::kDidFinishLoading,
       inflight_keepalive_bytes_, should_report_corb_blocking,
       cors_preflight_timing_info);
 }
@@ -1383,7 +1383,7 @@
   blob_finished_ = true;
   if (deferred_finish_loading_info_) {
     const ResourceResponse& response = resource_->GetResponse();
-    DidFinishLoading(deferred_finish_loading_info_->finish_time,
+    DidFinishLoading(deferred_finish_loading_info_->response_end,
                      response.EncodedDataLength(), response.EncodedBodyLength(),
                      response.DecodedBodyLength(),
                      deferred_finish_loading_info_->should_report_corb_blocking,
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
index 498766f0..3b83852f2 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -135,7 +135,7 @@
   void DidStartLoadingResponseBody(
       mojo::ScopedDataPipeConsumerHandle body) override;
   void DidFinishLoading(
-      TimeTicks finish_time,
+      TimeTicks response_end,
       int64_t encoded_data_length,
       int64_t encoded_body_length,
       int64_t decoded_body_length,
@@ -244,7 +244,7 @@
   // struct is used to store the information needed to refire DidFinishLoading
   // when the blob is finished too.
   struct DeferredFinishLoadingInfo {
-    TimeTicks finish_time;
+    TimeTicks response_end;
     bool should_report_corb_blocking;
     std::vector<network::cors::PreflightTimingInfo> cors_preflight_timing_info;
   };
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h b/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
index 59a7c71..7094d25 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
@@ -56,8 +56,8 @@
 
   const AtomicString& InitiatorType() const { return type_; }
 
-  void SetLoadFinishTime(TimeTicks time) { load_finish_time_ = time; }
-  TimeTicks LoadFinishTime() const { return load_finish_time_; }
+  void SetLoadResponseEnd(TimeTicks time) { load_response_end_ = time; }
+  TimeTicks LoadResponseEnd() const { return load_response_end_; }
 
   void SetInitialURL(const KURL& url) { initial_url_ = url; }
   const KURL& InitialURL() const { return initial_url_; }
@@ -95,7 +95,7 @@
 
   AtomicString type_;
   TimeTicks initial_time_;
-  TimeTicks load_finish_time_;
+  TimeTicks load_response_end_;
   KURL initial_url_;
   ResourceResponse final_response_;
   Vector<ResourceResponse> redirect_chain_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc b/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc
index 9a63f9b..ab2f54a1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc
@@ -21,11 +21,11 @@
     GetMemoryCache()->Remove(stale_resource_);
   ClearResource();
 
-  TimeTicks finish_time = resource->LoadFinishTime();
-  if (!finish_time.is_null()) {
+  TimeTicks response_end = resource->LoadResponseEnd();
+  if (!response_end.is_null()) {
     UMA_HISTOGRAM_LONG_TIMES(
         "Blink.ResourceFetcher.StaleWhileRevalidateDuration",
-        finish_time - start_time_);
+        response_end - start_time_);
   }
 }
 
diff --git a/third_party/blink/renderer/platform/network/http_parsers.cc b/third_party/blink/renderer/platform/network/http_parsers.cc
index b064ed9..6cc666a 100644
--- a/third_party/blink/renderer/platform/network/http_parsers.cc
+++ b/third_party/blink/renderer/platform/network/http_parsers.cc
@@ -583,10 +583,10 @@
                                    wtf_size_t* end) {
   DCHECK(IsMainThread());
 
-  int headers_end_pos =
+  size_t headers_end_pos =
       net::HttpUtil::LocateEndOfAdditionalHeaders(bytes, size, 0);
 
-  if (headers_end_pos < 0)
+  if (headers_end_pos == std::string::npos)
     return false;
 
   *end = static_cast<wtf_size_t>(headers_end_pos);
@@ -597,8 +597,8 @@
   headers.append(bytes, headers_end_pos);
 
   scoped_refptr<net::HttpResponseHeaders> response_headers =
-      new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
-          headers.data(), static_cast<int>(headers.length())));
+      new net::HttpResponseHeaders(
+          net::HttpUtil::AssembleRawHeaders(headers.data(), headers.length()));
 
   std::string mime_type, charset;
   response_headers->GetMimeTypeAndCharset(&mime_type, &charset);
@@ -627,10 +627,10 @@
                                        wtf_size_t* end) {
   DCHECK_EQ(0u, header_fields->size());
 
-  int headers_end_pos =
+  size_t headers_end_pos =
       net::HttpUtil::LocateEndOfAdditionalHeaders(bytes, size, 0);
 
-  if (headers_end_pos < 0)
+  if (headers_end_pos == std::string::npos)
     return false;
 
   *end = static_cast<wtf_size_t>(headers_end_pos);
@@ -641,8 +641,8 @@
   headers.append(bytes, headers_end_pos);
 
   scoped_refptr<net::HttpResponseHeaders> responseHeaders =
-      new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
-          headers.data(), static_cast<wtf_size_t>(headers.length())));
+      new net::HttpResponseHeaders(
+          net::HttpUtil::AssembleRawHeaders(headers.data(), headers.length()));
 
   // Copy selected header fields.
   const AtomicString* const headerNamePointers[] = {
diff --git a/third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h b/third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h
index a070892..864fda4 100644
--- a/third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h
+++ b/third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h
@@ -7,6 +7,7 @@
 
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -14,6 +15,8 @@
 // A CancelableClosureHolder is a CancelableCallback which resets its wrapped
 // callback with a cached closure whenever it is canceled.
 class CancelableClosureHolder {
+  DISALLOW_NEW();
+
  public:
   CancelableClosureHolder();
   ~CancelableClosureHolder();
diff --git a/third_party/blink/renderer/platform/scheduler/common/thread_load_tracker.h b/third_party/blink/renderer/platform/scheduler/common/thread_load_tracker.h
index 35014aaa..258a33130 100644
--- a/third_party/blink/renderer/platform/scheduler/common/thread_load_tracker.h
+++ b/third_party/blink/renderer/platform/scheduler/common/thread_load_tracker.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -19,6 +20,8 @@
 // Every |reporting_interval_| time units, it reports the average thread load
 // level computed using a sliding window of width |reporting_interval_|.
 class PLATFORM_EXPORT ThreadLoadTracker {
+  DISALLOW_NEW();
+
  public:
   // Callback is called with (current_time, load_level) parameters.
   using Callback = base::RepeatingCallback<void(base::TimeTicks, double)>;
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h b/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
index 807c4ae4..9f3e7c0a 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h
@@ -13,6 +13,7 @@
 #include "base/task/sequence_manager/lazy_now.h"
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace sequence_manager {
@@ -33,6 +34,8 @@
 // on a resource. This limit applies when task queues are already throttled
 // by TaskQueueThrottler.
 class PLATFORM_EXPORT BudgetPool {
+  USING_FAST_MALLOC(BudgetPool);
+
  public:
   virtual ~BudgetPool();
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h
index a59ef9a..0bbbc4e 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h
@@ -11,12 +11,15 @@
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
 
 // Runs a posted task at latest by a given deadline, but possibly sooner.
 class PLATFORM_EXPORT DeadlineTaskRunner {
+  USING_FAST_MALLOC(DeadlineTaskRunner);
+
  public:
   DeadlineTaskRunner(const base::RepeatingClosure& callback,
                      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
index 336e778e..c80ac68 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h
@@ -14,6 +14,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
@@ -38,6 +39,8 @@
 // MainThreadTaskQueues for non-loading queues, for accessing task queues and
 // their related voters, and for creating new task queues.
 class PLATFORM_EXPORT FrameTaskQueueController {
+  USING_FAST_MALLOC(FrameTaskQueueController);
+
  public:
   using TaskQueueAndEnabledVoterPair =
       std::pair<MainThreadTaskQueue*,
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 e2c2a5cb..5fe86ec 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
@@ -45,6 +45,7 @@
 #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"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace trace_event {
@@ -455,6 +456,8 @@
   };
 
   class Policy {
+    DISALLOW_NEW();
+
    public:
     Policy();
     ~Policy() = default;
@@ -548,24 +551,6 @@
         policies_;
   };
 
-  class PollableNeedsUpdateFlag {
-   public:
-    explicit PollableNeedsUpdateFlag(base::Lock* write_lock);
-    ~PollableNeedsUpdateFlag();
-
-    // Set the flag. May only be called if |write_lock| is held.
-    void SetWhileLocked(bool value);
-
-    // Returns true iff the flag is set to true.
-    bool IsSet() const;
-
-   private:
-    base::subtle::Atomic32 flag_;
-    base::Lock* write_lock_;  // Not owned.
-
-    DISALLOW_COPY_AND_ASSIGN(PollableNeedsUpdateFlag);
-  };
-
   class TaskDurationMetricTracker;
 
   class RendererPauseHandleImpl : public ThreadScheduler::RendererPauseHandle {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index 630cd7b..e9e0e9a 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h"
 #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace trace_event {
@@ -155,6 +156,8 @@
   };
 
   class PageLifecycleStateTracker {
+    USING_FAST_MALLOC(PageLifecycleStateTracker);
+
    public:
     explicit PageLifecycleStateTracker(PageSchedulerImpl*, PageLifecycleState);
     ~PageLifecycleStateTracker() = default;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h
index 46bf3336..64608d7 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h
@@ -15,10 +15,14 @@
 namespace scheduler {
 
 class PLATFORM_EXPORT PendingUserInput {
+  DISALLOW_NEW();
+
  public:
   // Handles the dispatch of WebInputEvents from the main thread scheduler,
   // keeping track of the set of in-flight input events.
   class PLATFORM_EXPORT Monitor {
+    DISALLOW_NEW();
+
    public:
     Monitor() { this->counters_.fill(0); }
     void OnEnqueue(WebInputEvent::Type);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h b/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h
index 6caf61e594..8a95c20 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h
@@ -7,6 +7,7 @@
 
 #include "base/task/sequence_manager/task_queue.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -15,6 +16,8 @@
 class MainThreadTaskQueue;
 
 class PLATFORM_EXPORT PrioritizeCompositingAfterInputExperiment {
+  DISALLOW_NEW();
+
  public:
   explicit PrioritizeCompositingAfterInputExperiment(
       MainThreadSchedulerImpl* scheduler);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h
index d8554dd..9bc8bc589 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_status.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -20,6 +21,8 @@
 // Records the expected queueing time for a high priority task occurring
 // randomly during each interval of length equal to window's duration.
 class PLATFORM_EXPORT QueueingTimeEstimator {
+  DISALLOW_NEW();
+
  public:
   class PLATFORM_EXPORT Client {
    public:
@@ -36,6 +39,8 @@
   };
 
   class RunningAverage {
+    DISALLOW_NEW();
+
    public:
     explicit RunningAverage(int steps_per_window);
     int GetStepsPerWindow() const;
@@ -50,6 +55,8 @@
   };
 
   class PLATFORM_EXPORT Calculator {
+    DISALLOW_NEW();
+
    public:
     explicit Calculator(int steps_per_window);
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h
index 6674227..feed3ae 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h
@@ -10,6 +10,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -17,6 +18,8 @@
 class WebRenderWidgetSchedulingState;
 
 class PLATFORM_EXPORT RenderWidgetSignals {
+  USING_FAST_MALLOC(RenderWidgetSignals);
+
  public:
   class PLATFORM_EXPORT Observer {
    public:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h
index 81270ec..4f520427 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_TYPE_NAMES_H_
 
 #include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -14,11 +15,10 @@
 namespace scheduler {
 
 class TaskTypeNames {
+  STATIC_ONLY(TaskTypeNames);
+
  public:
   static const char* TaskTypeToString(TaskType task_type);
-
- private:
-  TaskTypeNames();
 };
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h b/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h
index 32ad7b6..6f55b44 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h
@@ -11,11 +11,14 @@
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
 
 class PLATFORM_EXPORT UserModel {
+  USING_FAST_MALLOC(UserModel);
+
  public:
   UserModel();
   ~UserModel();
diff --git a/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h b/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h
index 585c28f..8ccca07 100644
--- a/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h
+++ b/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h
@@ -12,6 +12,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
@@ -19,6 +20,8 @@
 // PostCancellableDelayedTask() and cancels the associated task on
 // TaskHandle::cancel() call or on TaskHandle destruction.
 class PLATFORM_EXPORT TaskHandle {
+  DISALLOW_NEW();
+
  public:
   class Runner;
 
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread.h b/third_party/blink/renderer/platform/scheduler/public/thread.h
index 0774757..b3ad7f0 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread.h
@@ -32,6 +32,7 @@
 #include "base/threading/thread.h"
 #include "third_party/blink/public/platform/web_thread_type.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -70,6 +71,8 @@
 // Deleting the thread blocks until all pending, non-delayed tasks have been
 // run.
 class PLATFORM_EXPORT Thread {
+  USING_FAST_MALLOC(Thread);
+
  public:
   friend class Platform;  // For SetMainThread() and IsSimpleMainThread().
   friend class ScopedMainThreadOverrider;  // For SetMainThread().
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_cpu_throttler.h b/third_party/blink/renderer/platform/scheduler/public/thread_cpu_throttler.h
index 03b1105e..10b1c4b 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_cpu_throttler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_cpu_throttler.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 template <typename T>
@@ -23,6 +24,8 @@
 // additional thread which frequently interrupts main thread
 // and sleeps.
 class PLATFORM_EXPORT ThreadCPUThrottler final {
+  USING_FAST_MALLOC(ThreadCPUThrottler);
+
  public:
   static ThreadCPUThrottler* GetInstance();
 
diff --git a/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h
index 738f21d8..63ef7485 100644
--- a/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -29,6 +30,8 @@
   ~WorkerScheduler() override;
 
   class PLATFORM_EXPORT PauseHandle {
+    USING_FAST_MALLOC(PauseHandle);
+
    public:
     PauseHandle(base::WeakPtr<WorkerScheduler>);
     ~PauseHandle();
diff --git a/third_party/blink/renderer/platform/scheduler/test/DEPS b/third_party/blink/renderer/platform/scheduler/test/DEPS
index f97ca86..545b352 100644
--- a/third_party/blink/renderer/platform/scheduler/test/DEPS
+++ b/third_party/blink/renderer/platform/scheduler/test/DEPS
@@ -1,7 +1,3 @@
-include_rules = [
-  "+cc/base/lap_timer.h"
-]
-
 specific_include_rules = {
  "renderer_scheduler_test_support\.cc": [
    "+base/task/sequence_manager/test/lazy_thread_controller_for_test.h",
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
index fb070668..70379c3 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 namespace scheduler {
@@ -58,6 +59,8 @@
   ~FakeFrameScheduler() override = default;
 
   class Builder {
+    USING_FAST_MALLOC(Builder);
+
    public:
     Builder() = default;
 
diff --git a/third_party/blink/renderer/platform/scheduler/test/fuzzer/sequence_manager_fuzzer_processor.h b/third_party/blink/renderer/platform/scheduler/test/fuzzer/sequence_manager_fuzzer_processor.h
index 627836e..a150d20f 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fuzzer/sequence_manager_fuzzer_processor.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fuzzer/sequence_manager_fuzzer_processor.h
@@ -11,6 +11,7 @@
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fuzzer/proto/sequence_manager_test_description.pb.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace sequence_manager {
@@ -25,6 +26,8 @@
 // by the |thread_pool_manager_| should live for the scope of the main thread
 // entry function i.e RunTest.
 class PLATFORM_EXPORT SequenceManagerFuzzerProcessor {
+  DISALLOW_NEW();
+
  public:
   // Public interface used to parse the fuzzer's test description and
   // run the relevant APIs.
diff --git a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.h b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.h
index fd53e91..3b7351a9 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.h
@@ -19,12 +19,15 @@
 #include "third_party/blink/renderer/platform/scheduler/test/fuzzer/proto/sequence_manager_test_description.pb.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fuzzer/sequence_manager_fuzzer_processor.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fuzzer/task_queue_with_voters.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace sequence_manager {
 
 // Used by the SequenceManagerFuzzerProcessor to execute actions on a thread.
 class PLATFORM_EXPORT ThreadManager {
+  USING_FAST_MALLOC(ThreadManager);
+
  public:
   // |initial_time| is the time in which |this| was instantiated.
   ThreadManager(TimeTicks initial_time,
@@ -62,6 +65,8 @@
 
  protected:
   class Task {
+    USING_FAST_MALLOC(Task);
+
    public:
     explicit Task(ThreadManager* thread_manager);
     ~Task() = default;
diff --git a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_pool_manager.h b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_pool_manager.h
index a802c9ac..5644fda 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_pool_manager.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_pool_manager.h
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fuzzer/proto/sequence_manager_test_description.pb.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace base {
 namespace sequence_manager {
@@ -24,6 +25,8 @@
 // Used by the SequenceManagerFuzzerProcessor to manage threads and synchronize
 // their clocks.
 class PLATFORM_EXPORT ThreadPoolManager {
+  USING_FAST_MALLOC(ThreadPoolManager);
+
  public:
   explicit ThreadPoolManager(SequenceManagerFuzzerProcessor* processor);
   ~ThreadPoolManager();
diff --git a/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc b/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc
index 6cd179e..59a75ee 100644
--- a/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/queueing_time_estimator_perf_test.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
 #include "third_party/blink/renderer/platform/scheduler/test/test_queueing_time_estimator_client.h"
@@ -21,7 +21,7 @@
  public:
   QueueingTimeEstimatorTestPerfTest()
       : timer_(kWarmupRuns, kTimeLimit, kCheckInterval) {}
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
   base::TimeTicks time;
   TestQueueingTimeEstimatorClient client;
 };
diff --git a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
index ce5241c..e635da9 100644
--- a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
+++ b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
-
+#include "base/timer/lap_timer.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
@@ -58,7 +57,7 @@
       {roboto, "third_party/Roboto/roboto-regular.woff2"},
   };
 
-  cc::LapTimer timer;
+  base::LapTimer timer;
 };
 
 class OffsetForPositionPerfTest : public ShapeResultPerfTest,
diff --git a/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc b/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc
index 9ce98802..c265e21 100644
--- a/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc
+++ b/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc
@@ -5,8 +5,9 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h"
 
 #include <unicode/uscript.h>
+
 #include "base/time/time.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_test_utilities.h"
@@ -77,7 +78,7 @@
   unsigned start_index = 0;
   unsigned num_glyphs = 0;
   hb_script_t script = HB_SCRIPT_INVALID;
-  cc::LapTimer timer_;
+  base::LapTimer timer_;
 };
 
 TEST_F(ShapingLineBreakerPerfTest, ShapeLatinText) {
diff --git a/third_party/blink/renderer/platform/wtf/math_extras.h b/third_party/blink/renderer/platform/wtf/math_extras.h
index 6913d3dc..6ff8162 100644
--- a/third_party/blink/renderer/platform/wtf/math_extras.h
+++ b/third_party/blink/renderer/platform/wtf/math_extras.h
@@ -145,7 +145,7 @@
   return (value <= min) ? min : static_cast<LimitType>(value);
 }
 
-// For any floating-point limits, or integral limits smaller than long long, we
+// For any floating-point limits, or integral limits smaller than int64_t, we
 // can cast the limits to double without losing precision; then the only cases
 // where |value| can't be represented accurately as a double are the ones where
 // it's outside the limit range anyway.  So doing all comparisons as doubles
@@ -193,8 +193,8 @@
 };
 
 // The unspecialized version of this templated class handles clamping to
-// anything other than [unsigned] long long int limits.  It simply uses the
-// class above to toggle between the "fast" and "safe" clamp implementations.
+// anything other than [u]int64_t limits.  It simply uses the class above
+// to toggle between the "fast" and "safe" clamp implementations.
 template <typename LimitType, typename ValueType>
 class ClampToHelper {
  public:
@@ -224,75 +224,66 @@
   }
 };
 
-// Clamping to [unsigned] long long int limits requires more care.  These may
-// not be accurately representable as doubles, so instead we cast |value| to the
-// limit type.  But that cast is undefined if |value| is floating point and
+// Clamping to [u]int64_t limits requires more care.  These may not be
+// accurately representable as doubles, so instead we cast |value| to the
+// limit type. But that cast is undefined if |value| is floating point and
 // outside the representable range of the limit type, so we also have to check
 // for that case explicitly.
 template <typename ValueType>
-class ClampToHelper<long long int, ValueType> {
+class ClampToHelper<int64_t, ValueType> {
   STATIC_ONLY(ClampToHelper);
 
  public:
-  static inline long long int clampTo(ValueType value,
-                                      long long int min,
-                                      long long int max) {
+  static inline int64_t clampTo(ValueType value, int64_t min, int64_t max) {
     if (!std::numeric_limits<ValueType>::is_integer) {
       if (value > 0) {
         if (static_cast<double>(value) >=
-            static_cast<double>(std::numeric_limits<long long int>::max()))
+            static_cast<double>(std::numeric_limits<int64_t>::max()))
           return max;
       } else if (static_cast<double>(value) <=
-                 static_cast<double>(
-                     std::numeric_limits<long long int>::min())) {
+                 static_cast<double>(std::numeric_limits<int64_t>::min())) {
         return min;
       }
     }
-    // Note: If |value| were unsigned long long int, it could be larger than
-    // the largest long long int, and this code would be wrong; we handle
-    // this case with a separate full specialization below.
-    return clampToDirectComparison(static_cast<long long int>(value), min, max);
+    // Note: If |value| were uint64_t it could be larger than the largest
+    // int64_t, and this code would be wrong; we handle  this case with
+    // a separate full specialization below.
+    return clampToDirectComparison(static_cast<int64_t>(value), min, max);
   }
 };
 
 // This specialization handles the case where the above partial specialization
 // would be potentially incorrect.
 template <>
-class ClampToHelper<long long int, unsigned long long int> {
+class ClampToHelper<int64_t, uint64_t> {
   STATIC_ONLY(ClampToHelper);
 
  public:
-  static inline long long int clampTo(unsigned long long int value,
-                                      long long int min,
-                                      long long int max) {
-    if (max <= 0 || value >= static_cast<unsigned long long int>(max))
+  static inline int64_t clampTo(uint64_t value, int64_t min, int64_t max) {
+    if (max <= 0 || value >= static_cast<uint64_t>(max))
       return max;
-    const long long int longLongValue = static_cast<long long int>(value);
+    const int64_t longLongValue = static_cast<int64_t>(value);
     return (longLongValue <= min) ? min : longLongValue;
   }
 };
 
-// This is similar to the partial specialization that clamps to long long int,
-// but because the lower-bound check is done for integer value types as well, we
-// don't need a <unsigned long long int, long long int> full specialization.
+// This is similar to the partial specialization that clamps to int64_t, but
+// because the lower-bound check is done for integer value types as well, we
+// don't need a <uint64_t, int64_t> full specialization.
 template <typename ValueType>
-class ClampToHelper<unsigned long long int, ValueType> {
+class ClampToHelper<uint64_t, ValueType> {
   STATIC_ONLY(ClampToHelper);
 
  public:
-  static inline unsigned long long int clampTo(ValueType value,
-                                               unsigned long long int min,
-                                               unsigned long long int max) {
+  static inline uint64_t clampTo(ValueType value, uint64_t min, uint64_t max) {
     if (value <= 0)
       return min;
     if (!std::numeric_limits<ValueType>::is_integer) {
       if (static_cast<double>(value) >=
-          static_cast<double>(
-              std::numeric_limits<unsigned long long int>::max()))
+          static_cast<double>(std::numeric_limits<uint64_t>::max()))
         return max;
     }
-    return clampToDirectComparison(static_cast<unsigned long long int>(value),
-                                   min, max);
+    return clampToDirectComparison(static_cast<uint64_t>(value), min, max);
   }
 };
 
diff --git a/third_party/blink/renderer/platform/wtf/math_extras_test.cc b/third_party/blink/renderer/platform/wtf/math_extras_test.cc
index e90ac43..64f2b9c 100644
--- a/third_party/blink/renderer/platform/wtf/math_extras_test.cc
+++ b/third_party/blink/renderer/platform/wtf/math_extras_test.cc
@@ -71,10 +71,10 @@
 }
 
 TEST(MathExtrasTest, clampToIntLongLong) {
-  long long max_int = std::numeric_limits<int>::max();
-  long long min_int = std::numeric_limits<int>::min();
-  long long overflow_int = max_int + 1;
-  long long underflow_int = min_int - 1;
+  int64_t max_int = std::numeric_limits<int>::max();
+  int64_t min_int = std::numeric_limits<int>::min();
+  int64_t overflow_int = max_int + 1;
+  int64_t underflow_int = min_int - 1;
 
   EXPECT_GT(overflow_int, max_int);
   EXPECT_LT(underflow_int, min_int);
@@ -158,26 +158,25 @@
   EXPECT_EQ(0.0, clampTo<double>(0));
   EXPECT_EQ(0.0, clampTo<double>(0.0f));
   EXPECT_EQ(0.0, clampTo<double>(0ULL));
-  EXPECT_EQ(3.5, clampTo<double>(std::numeric_limits<unsigned long long>::max(),
-                                 0.0, 3.5));
+  EXPECT_EQ(3.5,
+            clampTo<double>(std::numeric_limits<uint64_t>::max(), 0.0, 3.5));
 }
 
 TEST(MathExtrasText, clampToLongLongDouble) {
   double overflow_ll =
-      static_cast<double>(std::numeric_limits<long long>::max()) * 2;
-  EXPECT_EQ(std::numeric_limits<long long>::max(),
-            clampTo<long long>(overflow_ll));
-  EXPECT_EQ(std::numeric_limits<long long>::min(),
-            clampTo<long long>(-overflow_ll));
+      static_cast<double>(std::numeric_limits<int64_t>::max()) * 2;
+  EXPECT_EQ(std::numeric_limits<int64_t>::max(), clampTo<int64_t>(overflow_ll));
+  EXPECT_EQ(std::numeric_limits<int64_t>::min(),
+            clampTo<int64_t>(-overflow_ll));
 }
 
 TEST(MathExtrasText, clampToUnsignedLongLongDouble) {
   double overflow_ull =
-      static_cast<double>(std::numeric_limits<unsigned long long>::max()) * 2;
-  EXPECT_EQ(std::numeric_limits<unsigned long long>::max(),
-            clampTo<unsigned long long>(overflow_ull));
-  EXPECT_EQ(std::numeric_limits<unsigned long long>::min(),
-            clampTo<unsigned long long>(-overflow_ull));
+      static_cast<double>(std::numeric_limits<uint64_t>::max()) * 2;
+  EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+            clampTo<uint64_t>(overflow_ull));
+  EXPECT_EQ(std::numeric_limits<uint64_t>::min(),
+            clampTo<uint64_t>(-overflow_ull));
 }
 
 TEST(MathExtrasTest, clampToUnsignedUnsignedLong) {
@@ -196,8 +195,8 @@
 }
 
 TEST(MathExtrasTest, clampToUnsignedUnsignedLongLong) {
-  unsigned long long max_unsigned = std::numeric_limits<unsigned>::max();
-  unsigned long long overflow_unsigned = max_unsigned + 1;
+  uint64_t max_unsigned = std::numeric_limits<unsigned>::max();
+  uint64_t overflow_unsigned = max_unsigned + 1;
 
   EXPECT_GT(overflow_unsigned, max_unsigned);
 
@@ -208,31 +207,30 @@
 }
 
 TEST(MathExtrasTest, clampToLongLongUnsignedLongLong) {
-  long long max_long_long_ll = std::numeric_limits<long long>::max();
-  unsigned long long max_long_long_ull = max_long_long_ll;
-  unsigned long long overflow_long_long = max_long_long_ull + 1;
+  int64_t max_long_long_ll = std::numeric_limits<int64_t>::max();
+  uint64_t max_long_long_ull = max_long_long_ll;
+  uint64_t overflow_long_long = max_long_long_ull + 1;
 
   EXPECT_GT(overflow_long_long, max_long_long_ull);
 
-  EXPECT_EQ(max_long_long_ll, clampTo<long long>(max_long_long_ull));
-  EXPECT_EQ(max_long_long_ll - 1, clampTo<long long>(max_long_long_ull - 1));
-  EXPECT_EQ(max_long_long_ll, clampTo<long long>(overflow_long_long));
+  EXPECT_EQ(max_long_long_ll, clampTo<int64_t>(max_long_long_ull));
+  EXPECT_EQ(max_long_long_ll - 1, clampTo<int64_t>(max_long_long_ull - 1));
+  EXPECT_EQ(max_long_long_ll, clampTo<int64_t>(overflow_long_long));
 
-  EXPECT_EQ(-3LL, clampTo<long long>(2ULL, -5LL, -3LL));
+  EXPECT_EQ(-3LL, clampTo<int64_t>(2ULL, -5LL, -3LL));
 }
 
 TEST(MathExtrasTest, clampToUnsignedLongLongInt) {
-  EXPECT_EQ(0ULL, clampTo<unsigned long long>(-1));
-  EXPECT_EQ(0ULL, clampTo<unsigned long long>(0));
-  EXPECT_EQ(1ULL, clampTo<unsigned long long>(1));
+  EXPECT_EQ(0ULL, clampTo<uint64_t>(-1));
+  EXPECT_EQ(0ULL, clampTo<uint64_t>(0));
+  EXPECT_EQ(1ULL, clampTo<uint64_t>(1));
 }
 
 TEST(MathExtrasTest, clampToUnsignedLongLongUnsignedLongLong) {
-  EXPECT_EQ(0ULL, clampTo<unsigned long long>(0ULL));
-  EXPECT_EQ(1ULL, clampTo<unsigned long long>(0ULL, 1ULL, 2ULL));
-  EXPECT_EQ(2ULL, clampTo<unsigned long long>(3ULL, 1ULL, 2ULL));
-  EXPECT_EQ(0xFFFFFFFFFFFFFFF5ULL,
-            clampTo<unsigned long long>(0xFFFFFFFFFFFFFFF5ULL));
+  EXPECT_EQ(0ULL, clampTo<uint64_t>(0ULL));
+  EXPECT_EQ(1ULL, clampTo<uint64_t>(0ULL, 1ULL, 2ULL));
+  EXPECT_EQ(2ULL, clampTo<uint64_t>(3ULL, 1ULL, 2ULL));
+  EXPECT_EQ(0xFFFFFFFFFFFFFFF5ULL, clampTo<uint64_t>(0xFFFFFFFFFFFFFFF5ULL));
 }
 
 // Make sure that various +-inf cases are handled properly (they weren't
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c6aee88..4d39582b 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3024,6 +3024,8 @@
 crbug.com/918664 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1a.html [ Failure Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.10 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html [ Failure ]
+crbug.com/626703 [ Mac10.11 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html [ Failure ]
 crbug.com/626703 external/wpt/html/semantics/forms/the-fieldset-element/accessibility/fieldset-div-display-contents-manual.html [ Skip ]
 crbug.com/626703 external/wpt/html/semantics/forms/the-fieldset-element/accessibility/role-manual.html [ Skip ]
 crbug.com/626703 external/wpt/html/semantics/forms/the-fieldset-element/accessibility/shadow-dom-manual.html [ Skip ]
@@ -5997,5 +5999,8 @@
 crbug.com/937991 [ Win7 Release ] http/tests/devtools/cache-storage/cache-data.js [ Pass Timeout ]
 
 # Sheriff 2019-03-05
-crbug.com/938200 http/tests/devtools/network/network-blocked-reason.js [ Timeout Pass ] http/tests/devtools/network/network-blocked-reason.js
+crbug.com/938200 http/tests/devtools/network/network-blocked-reason.js [ Timeout Pass ]
 crbug.com/938780 [ Mac ] external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html [ Failure ]
+
+# Sheriff 2019-03-06
+crbug.com/938591 [ Linux ] fast/events/middleClickAutoscroll-nested-divs-forbidden.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/accessibility/inline-text-changes.html b/third_party/blink/web_tests/accessibility/inline-text-changes.html
index 72151ca..7515f0b 100644
--- a/third_party/blink/web_tests/accessibility/inline-text-changes.html
+++ b/third_party/blink/web_tests/accessibility/inline-text-changes.html
@@ -30,7 +30,8 @@
     assert_equals(axInlineBefore1.name, 'two lines of text.');
 
     // Modify the text.
-    document.getElementById("p").innerText += ' One more sentence.';
+    var p = document.getElementById("p");
+    p.firstChild.data = p.innerText + ' One more sentence.';
 
     // Wait for a notification on the element before checking the new state.
     axStaticText.addNotificationListener(t.step_func((notification) => {
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index a2b6717..ef9a991 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -6403,12 +6403,6 @@
      {}
     ]
    ],
-   "uievents/click/click_event_target-manual.html": [
-    [
-     "/uievents/click/click_event_target-manual.html",
-     {}
-    ]
-   ],
    "uievents/interface/click-event-manual.htm": [
     [
      "/uievents/interface/click-event-manual.htm",
@@ -7011,6 +7005,30 @@
      {}
     ]
    ],
+   "animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden.https.html": [
+    [
+     "/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden.https.html",
+     [
+      [
+       "/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html": [
+    [
+     "/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html",
+     [
+      [
+       "/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "animation-worklet/worklet-animation-with-scroll-timeline.https.html": [
     [
      "/animation-worklet/worklet-animation-with-scroll-timeline.https.html",
@@ -40987,6 +41005,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/flex-basis-010.html": [
+    [
+     "/css/css-flexbox/flex-basis-010.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/flex-box-wrap.html": [
     [
      "/css/css-flexbox/flex-box-wrap.html",
@@ -41479,6 +41509,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/flex-minimum-height-flex-items-013.html": [
+    [
+     "/css/css-flexbox/flex-minimum-height-flex-items-013.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-flexbox/flex-minimum-width-flex-items-001.xht": [
     [
      "/css/css-flexbox/flex-minimum-width-flex-items-001.xht",
@@ -55767,6 +55809,18 @@
      {}
     ]
    ],
+   "css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html": [
+    [
+     "/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html",
+     [
+      [
+       "/css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-paint-api/background-image-alpha.https.html": [
     [
      "/css/css-paint-api/background-image-alpha.https.html",
@@ -117106,11 +117160,21 @@
      {}
     ]
    ],
+   "animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html": [
+    [
+     {}
+    ]
+   ],
    "animation-worklet/worklet-animation-with-scroll-timeline-ref.html": [
     [
      {}
     ]
    ],
+   "animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html": [
+    [
+     {}
+    ]
+   ],
    "apng/META.yml": [
     [
      {}
@@ -141421,6 +141485,11 @@
      {}
     ]
    ],
+   "css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-overscroll-behavior/META.yml": [
     [
      {}
@@ -186886,6 +186955,11 @@
      {}
     ]
    ],
+   "signed-exchange/resources/sxg-navigation-timing.html": [
+    [
+     {}
+    ]
+   ],
    "signed-exchange/resources/sxg-util.js": [
     [
      {}
@@ -187006,6 +187080,11 @@
      {}
     ]
    ],
+   "signed-exchange/resources/sxg/sxg-navigation-timing.sxg": [
+    [
+     {}
+    ]
+   ],
    "signed-exchange/resources/sxg/sxg-noncacheable.sxg": [
     [
      {}
@@ -195946,6 +196025,11 @@
      {}
     ]
    ],
+   "web-nfc/nfc_hw_disabled-manual.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "web-nfc/nfc_push.https-expected.txt": [
     [
      {}
@@ -200271,11 +200355,6 @@
      {}
     ]
    ],
-   "workers/constructors/Worker/unresolvable-url-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "workers/data-url-shared-window.html": [
     [
      {}
@@ -200751,11 +200830,46 @@
      {}
     ]
    ],
+   "workers/support/Worker-messageport.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/Worker-run-forever.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/Worker-structure-message.js": [
+    [
+     {}
+    ]
+   ],
    "workers/support/Worker-termination-with-port-messages.js": [
     [
      {}
     ]
    ],
+   "workers/support/Worker-thread-multi-port.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/Worker-timeout-cancel-order.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/Worker-timeout-decreasing-order.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/Worker-timeout-increasing-order.js": [
+    [
+     {}
+    ]
+   ],
    "workers/support/WorkerBasic.js": [
     [
      {}
@@ -200776,6 +200890,11 @@
      {}
     ]
    ],
+   "workers/support/WorkerGlobalScope-close.js": [
+    [
+     {}
+    ]
+   ],
    "workers/support/WorkerLocation-origin.html": [
     [
      {}
@@ -200811,11 +200930,31 @@
      {}
     ]
    ],
+   "workers/support/importScripts-1.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/importScripts-2.js": [
+    [
+     {}
+    ]
+   ],
+   "workers/support/importScripts-3.js": [
+    [
+     {}
+    ]
+   ],
    "workers/support/imported_script.py": [
     [
      {}
     ]
    ],
+   "workers/support/invalidScript.js": [
+    [
+     {}
+    ]
+   ],
    "workers/support/name-as-accidental-global.js": [
     [
      {}
@@ -221035,6 +221174,12 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-container-dynamic-002.html": [
+    [
+     "/css/css-position/position-absolute-container-dynamic-002.html",
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-container-dynamic.html": [
     [
      "/css/css-position/position-absolute-container-dynamic.html",
@@ -221047,6 +221192,12 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-crash-chrome-002.html": [
+    [
+     "/css/css-position/position-absolute-crash-chrome-002.html",
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-dynamic-containing-block.html": [
     [
      "/css/css-position/position-absolute-dynamic-containing-block.html",
@@ -287689,6 +287840,12 @@
      {}
     ]
    ],
+   "signed-exchange/sxg-navigation-timing.tentative.html": [
+    [
+     "/signed-exchange/sxg-navigation-timing.tentative.html",
+     {}
+    ]
+   ],
    "signed-exchange/sxg-non-secure-origin.tentative.html": [
     [
      "/signed-exchange/sxg-non-secure-origin.tentative.html",
@@ -290883,6 +291040,14 @@
      }
     ]
    ],
+   "uievents/click/click_event_target.html": [
+    [
+     "/uievents/click/click_event_target.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
    "uievents/click/mouse-dblclick-event.html": [
     [
      "/uievents/click/mouse-dblclick-event.html",
@@ -299089,6 +299254,24 @@
      {}
     ]
    ],
+   "workers/Worker-messageport.html": [
+    [
+     "/workers/Worker-messageport.html",
+     {}
+    ]
+   ],
+   "workers/Worker-multi-port.html": [
+    [
+     "/workers/Worker-multi-port.html",
+     {}
+    ]
+   ],
+   "workers/Worker-nested-importScripts-error.html": [
+    [
+     "/workers/Worker-nested-importScripts-error.html",
+     {}
+    ]
+   ],
    "workers/Worker-replace-global-constructor.any.js": [
     [
      "/workers/Worker-replace-global-constructor.any.serviceworker.html",
@@ -299123,12 +299306,48 @@
      {}
     ]
    ],
+   "workers/Worker-structure-message.html": [
+    [
+     "/workers/Worker-structure-message.html",
+     {}
+    ]
+   ],
+   "workers/Worker-terminate-forever.html": [
+    [
+     "/workers/Worker-terminate-forever.html",
+     {}
+    ]
+   ],
    "workers/Worker-termination-with-port-messages.html": [
     [
      "/workers/Worker-termination-with-port-messages.html",
      {}
     ]
    ],
+   "workers/Worker-timeout-cancel-order.html": [
+    [
+     "/workers/Worker-timeout-cancel-order.html",
+     {}
+    ]
+   ],
+   "workers/Worker-timeout-decreasing-order.html": [
+    [
+     "/workers/Worker-timeout-decreasing-order.html",
+     {}
+    ]
+   ],
+   "workers/Worker-timeout-increasing-order.html": [
+    [
+     "/workers/Worker-timeout-increasing-order.html",
+     {}
+    ]
+   ],
+   "workers/WorkerGlobalScope-close.html": [
+    [
+     "/workers/WorkerGlobalScope-close.html",
+     {}
+    ]
+   ],
    "workers/WorkerGlobalScope_ErrorEvent_colno.htm": [
     [
      "/workers/WorkerGlobalScope_ErrorEvent_colno.htm",
@@ -299153,12 +299372,6 @@
      {}
     ]
    ],
-   "workers/WorkerGlobalScope_close.htm": [
-    [
-     "/workers/WorkerGlobalScope_close.htm",
-     {}
-    ]
-   ],
    "workers/WorkerGlobalScope_importScripts.htm": [
     [
      "/workers/WorkerGlobalScope_importScripts.htm",
@@ -299587,6 +299800,12 @@
      {}
     ]
    ],
+   "workers/constructors/Worker/Worker-constructor.html": [
+    [
+     "/workers/constructors/Worker/Worker-constructor.html",
+     {}
+    ]
+   ],
    "workers/constructors/Worker/ctor-1.html": [
     [
      "/workers/constructors/Worker/ctor-1.html",
@@ -299611,18 +299830,6 @@
      {}
     ]
    ],
-   "workers/constructors/Worker/no-arguments-ctor.html": [
-    [
-     "/workers/constructors/Worker/no-arguments-ctor.html",
-     {}
-    ]
-   ],
-   "workers/constructors/Worker/resolve-empty-string.html": [
-    [
-     "/workers/constructors/Worker/resolve-empty-string.html",
-     {}
-    ]
-   ],
    "workers/constructors/Worker/same-origin.html": [
     [
      "/workers/constructors/Worker/same-origin.html",
@@ -299641,12 +299848,6 @@
      {}
     ]
    ],
-   "workers/constructors/Worker/unresolvable-url.html": [
-    [
-     "/workers/constructors/Worker/unresolvable-url.html",
-     {}
-    ]
-   ],
    "workers/constructors/Worker/use-base-url.html": [
     [
      "/workers/constructors/Worker/use-base-url.html",
@@ -317546,10 +317747,26 @@
    "6f981854d38877d42b1c7b63afdb9ec989a32d42",
    "reftest"
   ],
+  "animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html": [
+   "c6d7314e396e85225f245905f5afa17fb848b469",
+   "support"
+  ],
+  "animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden.https.html": [
+   "8c3ebb66434f49c6ce2f21b0c0976dacc89516f3",
+   "reftest"
+  ],
   "animation-worklet/worklet-animation-with-scroll-timeline-ref.html": [
    "fe92232d9afa24f78e9cc7cc3bae341ba2a471bc",
    "support"
   ],
+  "animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html": [
+   "5810e1738c1d5927223037e97a7a14a52c405a5e",
+   "support"
+  ],
+  "animation-worklet/worklet-animation-with-scroll-timeline-root-scroller.https.html": [
+   "be577dcc50f2cd462455bac1361bf4fab8b8a5ec",
+   "reftest"
+  ],
   "animation-worklet/worklet-animation-with-scroll-timeline.https.html": [
    "000517162af20406e39831afc0b6cefa0b367f2c",
    "reftest"
@@ -346618,6 +346835,10 @@
    "3e2026958fdd899349126ebe4778fb62a692d631",
    "testharness"
   ],
+  "css/css-flexbox/flex-basis-010.html": [
+   "79992974b054b553b9052cba32a16fd89c37a866",
+   "reftest"
+  ],
   "css/css-flexbox/flex-box-wrap.html": [
    "2a1ab9c00cda7b78026eb04e31894bda43392a5d",
    "reftest"
@@ -346858,6 +347079,10 @@
    "a193c29cb6001bfc1bc7dc2c7258a48ae9da17a6",
    "testharness"
   ],
+  "css/css-flexbox/flex-minimum-height-flex-items-013.html": [
+   "9989b6463780fad120724ab52998353958650c81",
+   "reftest"
+  ],
   "css/css-flexbox/flex-minimum-width-flex-items-001.xht": [
    "b8e2866edaa46af46900c287238894cd8ddef24c",
    "reftest"
@@ -364046,6 +364271,14 @@
    "de894fab610b31257d73ef5488c376e50d899fb9",
    "support"
   ],
+  "css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height-ref.html": [
+   "5c27ecce55ef0c3c2e6a9ae120dabab988b37a06",
+   "support"
+  ],
+  "css/css-overflow/webkit-line-clamp/webkit-line-clamp-with-line-height.tentative.html": [
+   "2cd7177ece8d2d0a2193c794fe6312b84568e337",
+   "reftest"
+  ],
   "css/css-overscroll-behavior/META.yml": [
    "97705c2df87277d9e5af97d53e453305449c94b8",
    "support"
@@ -364762,6 +364995,10 @@
    "2b158a86f6599e43f6a2315a2943b4d394405ba5",
    "testharness"
   ],
+  "css/css-position/position-absolute-container-dynamic-002.html": [
+   "91d862835e6d1351deefb26f7e2b71a9539bbd6c",
+   "testharness"
+  ],
   "css/css-position/position-absolute-container-dynamic.html": [
    "711d31766da2e324d3d5ddf0362bc1c71b76d7f3",
    "testharness"
@@ -364770,6 +365007,10 @@
    "592e5d22e70f1b0c5e3e4b9222cbd1ccef99bdef",
    "testharness"
   ],
+  "css/css-position/position-absolute-crash-chrome-002.html": [
+   "9a4ff0ae58349d7be0583b9e8ccc6bcf6104ab89",
+   "testharness"
+  ],
   "css/css-position/position-absolute-dynamic-containing-block.html": [
    "3968f685849663574ca213fcb90dc5fb3eaffaa3",
    "testharness"
@@ -444811,7 +445052,7 @@
    "testharness"
   ],
   "performance-timeline/po-observe.html": [
-   "d65c8a40c981e1e79db9096df3a6991ab62b1e0a",
+   "1132f12fc8883fec4599bf5ded70209ed1532920",
    "testharness"
   ],
   "performance-timeline/po-resource.html": [
@@ -460603,7 +460844,7 @@
    "support"
   ],
   "signed-exchange/resources/generate-test-sxgs.sh": [
-   "cc2b6dd5a215046d2943b041620ba656b69ea0b0",
+   "60746ff34caaec9e83942aeae6a6cc74aa8138e0",
    "support"
   ],
   "signed-exchange/resources/inner-url.html": [
@@ -460622,6 +460863,10 @@
    "b3ebd2bde7acd2d1885838a37ab9cfc91c250551",
    "support"
   ],
+  "signed-exchange/resources/sxg-navigation-timing.html": [
+   "ddbe3503548ae2915253ad1f0ccc62d4278bf17a",
+   "support"
+  ],
   "signed-exchange/resources/sxg-util.js": [
    "7abb3cf2079087092ac4a7f7332ecf72f9cabd75",
    "support"
@@ -460718,6 +460963,10 @@
    "0c6dce01da270bdacf5d280551d56d1519aff3a3",
    "support"
   ],
+  "signed-exchange/resources/sxg/sxg-navigation-timing.sxg": [
+   "56a90d5125e68a5cacb37a239b2b95f35f95c124",
+   "support"
+  ],
   "signed-exchange/resources/sxg/sxg-noncacheable.sxg": [
    "88439982f6044b3ddbd1a1e0198bebb6409b0837",
    "support"
@@ -460786,6 +461035,10 @@
    "5f923d205421ca30e360ddf0de41bb64733de1d3",
    "testharness"
   ],
+  "signed-exchange/sxg-navigation-timing.tentative.html": [
+   "b3f088241f8e2cf3467752d556d8cd58d30d5b07",
+   "testharness"
+  ],
   "signed-exchange/sxg-non-secure-origin.tentative.html": [
    "84938da76d09dbe4f045b763e49ba95a4936003f",
    "testharness"
@@ -468559,7 +468812,7 @@
    "testharness"
   ],
   "trusted-types/TrustedTypePolicyFactory-isXXX.tentative.html": [
-   "fada29a209b777eb6b34cb14fceb0bfd2d249142",
+   "854f69ed1e550bd660b8cf7a18b4a81a666072fd",
    "testharness"
   ],
   "trusted-types/Window-TrustedTypes.tentative.html": [
@@ -468682,9 +468935,9 @@
    "9f4ffed96e6cb186f8441e9681c281d087ff8faf",
    "testharness"
   ],
-  "uievents/click/click_event_target-manual.html": [
-   "1e5670262b5cb39cf74e34616f908d7cc3cc09cf",
-   "manual"
+  "uievents/click/click_event_target.html": [
+   "ffe5dbaf3988a4908cda76bf3f4edf901b24833b",
+   "testharness"
   ],
   "uievents/click/mouse-dblclick-event.html": [
    "5bf0bbc07e00d3d01bfcc1379b8ac0c2f78fb7b7",
@@ -470966,8 +471219,12 @@
    "ab0d6cd191a5b576674c0c038087ef17ece4cdfd",
    "testharness"
   ],
+  "web-nfc/nfc_hw_disabled-manual.https-expected.txt": [
+   "104d42a9e9a0b5d82ab2b21d9d701dd0a27b9dfa",
+   "support"
+  ],
   "web-nfc/nfc_hw_disabled-manual.https.html": [
-   "cd84961c91becdd5bfd29f78ca4f8af3b4d80973",
+   "bb2cd42f1e65cca49b03385e9e553cc8077aa08d",
    "manual"
   ],
   "web-nfc/nfc_insecure_context.html": [
@@ -479138,6 +479395,18 @@
    "2ef944553105767535f6e3119048091f959bfac8",
    "testharness"
   ],
+  "workers/Worker-messageport.html": [
+   "f7734b2543fc7ace5c2d330c9f1be4d45c8cb392",
+   "testharness"
+  ],
+  "workers/Worker-multi-port.html": [
+   "9a89583ad91b40e64c7a3682da1e5053e18b4d26",
+   "testharness"
+  ],
+  "workers/Worker-nested-importScripts-error.html": [
+   "8863b7523010dd8566d1ed42094e519efc500a65",
+   "testharness"
+  ],
   "workers/Worker-replace-global-constructor.any.js": [
    "f208f3736362a82a322527b2f983b9e3ccaff33e",
    "testharness"
@@ -479150,10 +479419,34 @@
    "4339f2e33436010e0f05ea3d2c5f4ec26039cef0",
    "testharness"
   ],
+  "workers/Worker-structure-message.html": [
+   "9eb12dc6c55bc8252ee92746259ef10f4b9a08be",
+   "testharness"
+  ],
+  "workers/Worker-terminate-forever.html": [
+   "3528bb6246dd9abd03b68702a1bb12a5616cadce",
+   "testharness"
+  ],
   "workers/Worker-termination-with-port-messages.html": [
    "bc19784adae8e060813da4cf6f20dc47ef87ecf4",
    "testharness"
   ],
+  "workers/Worker-timeout-cancel-order.html": [
+   "78d930df783b9572395c6dcf1ead552dbb7da898",
+   "testharness"
+  ],
+  "workers/Worker-timeout-decreasing-order.html": [
+   "f459461ec061f9e9dd3d829241b117686c053b5b",
+   "testharness"
+  ],
+  "workers/Worker-timeout-increasing-order.html": [
+   "34b029466bb29e8a3478e040641415354045fa0f",
+   "testharness"
+  ],
+  "workers/WorkerGlobalScope-close.html": [
+   "a193a3478f4a58176314727898593428a53726a3",
+   "testharness"
+  ],
   "workers/WorkerGlobalScope_ErrorEvent_colno.htm": [
    "793d6c1807b2966fb28da9cc491df5549d1382ed",
    "testharness"
@@ -479170,10 +479463,6 @@
    "bc1d2c45b42cb0e3cb96a243d00f2b7351e8b1d3",
    "testharness"
   ],
-  "workers/WorkerGlobalScope_close.htm": [
-   "2ad5205d1b90ff12b1b3d8e8d6bbbb20b33fbff8",
-   "testharness"
-  ],
   "workers/WorkerGlobalScope_importScripts.htm": [
    "1a3616df98bc4f9fee566990210203890a8d9af0",
    "testharness"
@@ -479590,6 +479879,10 @@
    "2ef466ccdc30484120ff81730d90bd13cb0e52eb",
    "testharness"
   ],
+  "workers/constructors/Worker/Worker-constructor.html": [
+   "e073df76f36f0117cdf61e04f05cc8cb0f5530ff",
+   "testharness"
+  ],
   "workers/constructors/Worker/ctor-1.html": [
    "bd865261fc6e50bb25f073d68c7c11c20ddcabe9",
    "testharness"
@@ -479610,18 +479903,10 @@
    "0ce41b59e7496dfda26053cdfac6bd090b40a3b6",
    "testharness"
   ],
-  "workers/constructors/Worker/no-arguments-ctor.html": [
-   "770c7cc93a928229e521117dbd54aa6a5a3263d6",
-   "testharness"
-  ],
   "workers/constructors/Worker/null": [
    "6d079b514cf50a42deb039a2503a6f7ca1f2f70d",
    "support"
   ],
-  "workers/constructors/Worker/resolve-empty-string.html": [
-   "0426223e0a81d72091d6df5a6cd60c7a9ccc4e2c",
-   "testharness"
-  ],
   "workers/constructors/Worker/same-origin.html": [
    "560b34679674a98a9639fdf613a25f4e8c537572",
    "testharness"
@@ -479642,14 +479927,6 @@
    "69d29b2297847124206b392fea67949106a8a3fe",
    "testharness"
   ],
-  "workers/constructors/Worker/unresolvable-url-expected.txt": [
-   "f6e2da6493fce113e82a939cad82cc656d774c3a",
-   "support"
-  ],
-  "workers/constructors/Worker/unresolvable-url.html": [
-   "8c04b0087839808e98f66b79797e38a2e58912f6",
-   "testharness"
-  ],
   "workers/constructors/Worker/use-base-url.html": [
    "94ce2a71f14296a66bec0325f2d1d22ba1355b16",
    "testharness"
@@ -480530,10 +480807,38 @@
    "f0b8efe46bd8c93e5c2af1e239139faae76a16be",
    "support"
   ],
+  "workers/support/Worker-messageport.js": [
+   "1e01b0a52b586b00cbbb28b2352959f7fa150e39",
+   "support"
+  ],
+  "workers/support/Worker-run-forever.js": [
+   "912e49de65081d216980a54d4f5479ff880079c4",
+   "support"
+  ],
+  "workers/support/Worker-structure-message.js": [
+   "81cd98243b4d6d21977da64d8fa858747b6ddbe3",
+   "support"
+  ],
   "workers/support/Worker-termination-with-port-messages.js": [
    "a827db357e2305b32716c447dc70883b8ff3184a",
    "support"
   ],
+  "workers/support/Worker-thread-multi-port.js": [
+   "2ce7cae596b561caf5efec6353ed2948d250756e",
+   "support"
+  ],
+  "workers/support/Worker-timeout-cancel-order.js": [
+   "afc66cf6a0b4597b6befb58262cedf15b1e55954",
+   "support"
+  ],
+  "workers/support/Worker-timeout-decreasing-order.js": [
+   "b93dd5dc771ab4e0901bf1c8f31680feba1f1bdd",
+   "support"
+  ],
+  "workers/support/Worker-timeout-increasing-order.js": [
+   "42f81daa29c021bd5b40472a93c5f5bd222548a7",
+   "support"
+  ],
   "workers/support/WorkerBasic.js": [
    "6bb6d32b141916146257bbad665e2526111a0849",
    "support"
@@ -480550,6 +480855,10 @@
    "4ad700bcce464611b3c3f07e8555b4c6ccb6cfd6",
    "support"
   ],
+  "workers/support/WorkerGlobalScope-close.js": [
+   "e2b1c0a2fddfeafd701d7b1a0cbcba87e69a51bc",
+   "support"
+  ],
   "workers/support/WorkerLocation-origin.html": [
    "3d948030a79e431efd8d33974ca0f2cd2092ddca",
    "support"
@@ -480578,10 +480887,26 @@
    "0fb0ec228079de8dd15626fb3161b53d48c68112",
    "support"
   ],
+  "workers/support/importScripts-1.js": [
+   "8869a4fbcafce79b72b8c18e3100dd1ee2a2f7a0",
+   "support"
+  ],
+  "workers/support/importScripts-2.js": [
+   "7f3f1116e76ce3f1d2aca375322185db7f2c3fc3",
+   "support"
+  ],
+  "workers/support/importScripts-3.js": [
+   "2d103a0411fea792112f52ddbbd90bc3fedab2ba",
+   "support"
+  ],
   "workers/support/imported_script.py": [
    "88cd2285e82162679c0bcbcc1ff756c91c12a5a0",
    "support"
   ],
+  "workers/support/invalidScript.js": [
+   "8655d4d1f18c83f20e4516f992b25646c7de5c5e",
+   "support"
+  ],
   "workers/support/name-as-accidental-global.js": [
    "f2c39ea715aa64a990eda923931969a890ce336d",
    "support"
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-display-none.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-display-none.https.html
index 6f98185..9841c575 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-display-none.https.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-display-none.https.html
@@ -61,10 +61,7 @@
     const animation = new WorkletAnimation('passthrough', effect, timeline);
     animation.play();
 
-    // Ensure that the WorkletAnimation will have been started on the compositor.
     waitForAsyncAnimationFrames(1).then(_ => {
-      // Now return the scroller to the world, which will cause it to be composited
-      // and the animation should update on the compositor side.
       scroller.classList.remove('removed');
       const maxScroll = scroller.scrollHeight - scroller.clientHeight;
       scroller.scrollTop = 0.5 * maxScroll;
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html
index c6d7314e..2004e6df 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-and-overflow-hidden-ref.html
@@ -7,7 +7,6 @@
     background-color: green;
     transform: translate(0, 100px);
     opacity: 0.5;
-    will-change: transform; /* force compositing */
   }
 
   #covered {
@@ -20,7 +19,6 @@
     overflow: hidden;
     height: 100px;
     width: 100px;
-    will-change: transform; /* force compositing */
   }
 
   #contents {
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-ref.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-ref.html
index fe92232..f30c861f 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-ref.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-ref.html
@@ -7,7 +7,6 @@
     background-color: green;
     transform: translate(0, 100px);
     opacity: 0.5;
-    will-change: transform; /* force compositing */
   }
 
   #covered {
@@ -20,7 +19,8 @@
     overflow: auto;
     height: 100px;
     width: 100px;
-    will-change: transform; /* force compositing */
+    /* TODO(yigu): Rewrite the test to not rely on compositing. */
+    will-change: transform;
   }
 
   #contents {
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html
index 5810e17..3b527dc 100644
--- a/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/worklet-animation-with-scroll-timeline-root-scroller-ref.html
@@ -14,7 +14,6 @@
     background-color: green;
     transform: translate(0, 100px);
     opacity: 0.5;
-    will-change: transform; /* force compositing */
   }
 
   #covered {
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
index 4d59d4af..1fd0277 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
@@ -20,12 +20,14 @@
     img.setAttribute('elementtiming', 'my_image');
     document.body.appendChild(img);
     window.onload = t.step_func_done( () => {
-      const entries = performance.getEntriesByName('my_image');
-      assert_equals(entries.length, 1);
-      assert_greater_than_equal(performance.getEntriesByType('element').length, 1);
-      assert_equals(performance.getEntries().filter(e => e.name === 'my_image').length, 1);
+      const entries = performance.getEntriesByType('element');
+      assert_greater_than_equal(entries.length, 1);
+      assert_equals(performance.getEntries().filter(e => e.identifier === 'my_image').length, 1);
       const entry = entries[0];
-      checkElement(entry, 'my_image', beforeRender);
+      const index = window.location.href.lastIndexOf('/');
+      const pathname = window.location.href.substring(0, index) +
+          '/resources/square20.jpg';
+      checkElement(entry, pathname, 'my_image', beforeRender);
     });
   }, "Element Timing: image loads before onload.");
 
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html
index 6d5abe2..3ba12a7d0 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html
@@ -13,11 +13,13 @@
 <script>
   async_test((t) => {
     let beforeRender;
+    const img_src = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/'
+        + 'resources/TAOImage.py?tao=wildcard';
     const observer = new PerformanceObserver(
       t.step_func_done((entryList) => {
         assert_equals(entryList.getEntries().length, 1);
         const entry = entryList.getEntries()[0];
-        checkElement(entry, 'my_image', beforeRender);
+        checkElement(entry, img_src, 'my_image', beforeRender);
         // Assume viewport has size at least 20, so the element is fully visible.
         checkRect(entry, [0, 20, 0, 20]);
       })
@@ -28,8 +30,7 @@
     // TODO(npm): change observer to use buffered flag.
     window.onload = t.step_func(() => {
       const img = document.createElement('img');
-      img.src = 'http://{{domains[www]}}:{{ports[http][1]}}/'
-          + 'element-timing/resources/TAOImage.py?tao=wildcard';
+      img.src = img_src;
       img.setAttribute('elementtiming', 'my_image');
       img.onload = t.step_func(() => {
         // After a short delay, assume that the entry was not dispatched.
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html b/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html
index 8ab3f343..d3e2c105 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html
@@ -17,7 +17,10 @@
       t.step_func_done(function(entryList) {
         assert_equals(entryList.getEntries().length, 1);
         const entry = entryList.getEntries()[0];
-        checkElement(entry, 'not_fully_visible', beforeRender);
+        const index = window.location.href.lastIndexOf('/');
+        const pathname = window.location.href.substring(0, index) +
+            '/resources/square20.png';
+        checkElement(entry, pathname, 'not_fully_visible', beforeRender);
         // Image will not be fully visible. It should start from the top left part
         // of the document, excluding the margin, and then overflow.
         checkRect(entry,
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html b/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html
new file mode 100644
index 0000000..18c72cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<meta charset=utf-8>
+<title>Element Timing: observe elements with the same resource</title>
+<body>
+<style>
+body {
+  margin: 0;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/element-timing-helpers.js"></script>
+<script>
+  let beforeRender;
+  let numEntries = 0;
+  let responseEnd1;
+  let responseEnd2;
+  const index = window.location.href.lastIndexOf('/');
+  const pathname = window.location.href.substring(0, index) +
+      '/resources/square100.png';
+  async_test(function (t) {
+    const observer = new PerformanceObserver(
+      t.step_func(function(entryList) {
+        entryList.getEntries().forEach(entry => {
+          checkElement(entry, pathname, entry.identifier, beforeRender);
+          if (entry.identifier === 'my_image') {
+            ++numEntries;
+            responseEnd1 = entry.responseEnd;
+          }
+          else if (entry.identifier === 'my_image2') {
+            ++numEntries;
+            responseEnd2 = entry.responseEnd;
+          }
+        });
+        if (numEntries == 2) {
+          assert_equals(responseEnd1, responseEnd2);
+          t.done();
+        }
+      })
+    );
+    observer.observe({entryTypes: ['element']});
+    // We add the images during onload to be sure that the observer is registered
+    // in time for it to observe the element timing.
+    window.onload = () => {
+      // Add image of width and height equal to 100.
+      const img = document.createElement('img');
+      img.src = 'resources/square100.png';
+      img.setAttribute('elementtiming', 'my_image');
+      document.body.appendChild(img);
+
+      const img2 = document.createElement('img');
+      img2.src = 'resources/square100.png';
+      img2.setAttribute('elementtiming', 'my_image2');
+      document.body.appendChild(img2);
+
+      beforeRender = performance.now();
+    };
+  }, 'Element with elementtiming attribute is observable.');
+</script>
+
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html
index c148d33a..9170b36 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html
@@ -17,7 +17,10 @@
       t.step_func_done(function(entryList) {
         assert_equals(entryList.getEntries().length, 1);
         const entry = entryList.getEntries()[0];
-        checkElement(entry, 'my_image', beforeRender);
+        const index = window.location.href.lastIndexOf('/');
+        const pathname = window.location.href.substring(0, index) +
+            '/resources/square100.png';
+        checkElement(entry, pathname, 'my_image', beforeRender);
         // Assume viewport has size at least 100, so the element is fully visible.
         checkRect(entry, [0, 100, 0, 100]);
       })
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html
index ef3eab8..fb288438 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html
@@ -17,7 +17,10 @@
       t.step_func_done(function(entryList) {
         assert_equals(entryList.getEntries().length, 1);
         const entry = entryList.getEntries()[0];
-        checkElement(entry, 'img', beforeRender)
+        const index = window.location.href.lastIndexOf('/');
+        const pathname = window.location.href.substring(0, index) +
+            '/resources/square20.jpg';
+        checkElement(entry, pathname, '', beforeRender);
         // Assume viewport hasn't changed, so the element occupies all of it.
         checkRect(entry,
           [0, document.documentElement.clientWidth, 0, document.documentElement.clientHeight]);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html
index e56092c6..aa91aa83 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html
@@ -22,16 +22,19 @@
 <script>
   let beforeRender, image1Observed=0, image2Observed=0, image3Observed=0;
   async_test(function (t) {
+    const index = window.location.href.lastIndexOf('/');
     const observer = new PerformanceObserver(
       t.step_func(function(entryList) {
         entryList.getEntries().forEach( entry => {
-          if (entry.name === 'image1') {
+          if (entry.identifier === 'image1') {
             if (image1Observed) {
               assert_unreached("Observer received image1 more than once");
               t.done();
             }
             image1Observed = 1;
-            checkElement(entry, 'image1', beforeRender);
+            const pathname1 = window.location.href.substring(0, index) +
+                '/resources/square100.png';
+            checkElement(entry, pathname1, 'image1', beforeRender);
             // This image is horizontally centered.
             // Using abs and comparing to 1 because the viewport sizes could be odd.
             // If a size is odd, then image cannot be in the pure center, but left
@@ -46,28 +49,32 @@
             assert_equals(entry.intersectionRect.bottom,
               100, 'bottom of rect for image1');
           }
-          else if (entry.name === 'image2') {
+          else if (entry.identifier === 'image2') {
             if (image2Observed) {
               assert_unreached("Observer received image2 more than once");
               t.done();
             }
             image2Observed = 1;
-            checkElement(entry, 'image2', beforeRender);
+            const pathname2 = window.location.href.substring(0, index) +
+                '/resources/square20.png';
+            checkElement(entry, pathname2, 'image2', beforeRender);
             // This image should be below image 1, and should respect the margin.
             checkRect(entry, [50, 250, 250, 450], "of image2");
           }
-          else if (entry.name === 'image3') {
+          else if (entry.identifier === 'image3') {
             if (image3Observed) {
               assert_unreached("Observer received image3 more than once");
               t.done();
             }
             image3Observed = 1;
-            checkElement(entry, 'image3', beforeRender);
+            const pathname3 = window.location.href.substring(0, index) +
+                '/resources/circle.svg';
+            checkElement(entry, pathname3, 'image3', beforeRender);
             // This image is just to the right of image2.
             checkRect(entry, [250, 450, 250, 450], "of image3");
           }
           else {
-            assert_unreached("Received an unexpected name.");
+            assert_unreached("Received an unexpected identifier.");
             t.done();
           }
           if (image1Observed && image2Observed && image3Observed) {
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html
index 327ab69..f127152c 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html
@@ -11,7 +11,10 @@
     t.step_func_done(function(entryList) {
       assert_equals(entryList.getEntries().length, 1);
       const entry = entryList.getEntries()[0];
-      checkElement(entry, 'my_svg', beforeRender);
+      const index = window.location.href.lastIndexOf('/');
+      const pathname = window.location.href.substring(0, index) +
+          '/resources/circle.svg';
+      checkElement(entry, pathname, 'my_svg', beforeRender);
       // Assume viewport has size at least 200, so the element is fully visible.
       checkRect(entry, [0, 200, 0, 200]);
     })
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html b/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html
index 6fdff39..cf54e1e 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html
+++ b/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html
@@ -13,20 +13,24 @@
   let numInitial = 75;
   let sleep = 500;
   async_test(function(t) {
+    const img_src = 'resources/progressive-image.py?name=square20.jpg&numInitial='
+      + numInitial + '&sleep=' + sleep;
     const observer = new PerformanceObserver(
       t.step_func_done(function(entryList) {
         assert_equals(entryList.getEntries().length, 1);
         const entry = entryList.getEntries()[0];
+        const index = window.location.href.lastIndexOf('/');
+        const pathname = window.location.href.substring(0, index) + '/' +
+            img_src;
         // Since the image is only fully loaded after the sleep, the render timestamp
         // must be greater than |beforeRender| + |sleep|.
-        checkElement(entry, 'my_image', beforeRender + sleep);
+        checkElement(entry, pathname, 'my_image', beforeRender + sleep);
       })
     );
     observer.observe({entryTypes: ['element']});
 
     const img = document.createElement('img');
-    img.src = 'resources/progressive-image.py?name=square20.jpg&numInitial='
-      + numInitial + '&sleep=' + sleep;
+    img.src = img_src;
     img.setAttribute('elementtiming', 'my_image');
     document.body.appendChild(img);
     beforeRender = performance.now();
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
index 0be97e4..e952930 100644
--- a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
+++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
@@ -1,11 +1,15 @@
 // Checks that this is an ElementTiming entry with name |expectedName|. It also
 // does a very basic check on |startTime|: after |beforeRender| and before now().
-function checkElement(entry, expectedName, beforeRender) {
+function checkElement(entry, expectedName, expectedIdentifier, beforeRender) {
   assert_equals(entry.entryType, 'element');
   assert_equals(entry.name, expectedName);
+  assert_equals(entry.identifier, expectedIdentifier);
   assert_equals(entry.duration, 0);
   assert_greater_than_equal(entry.startTime, beforeRender);
   assert_greater_than_equal(performance.now(), entry.startTime);
+  const rt_entries = performance.getEntriesByName(expectedName, 'resource');
+  assert_equals(rt_entries.length, 1);
+  assert_equals(rt_entries[0].responseEnd, entry.responseEnd);
 }
 
 // Checks that the rect matches the desired values [left right top bottom]
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/elements/the-innertext-idl-attribute/setter-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/elements/the-innertext-idl-attribute/setter-expected.txt
deleted file mode 100644
index e263930..0000000
--- a/third_party/blink/web_tests/external/wpt/html/dom/elements/the-innertext-idl-attribute/setter-expected.txt
+++ /dev/null
@@ -1,130 +0,0 @@
-This is a testharness.js-based test.
-Found 126 tests; 95 PASS, 31 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Simplest possible test
-PASS Simplest possible test, detached
-PASS Newlines convert to <br> in non-white-space:pre elements
-PASS Newlines convert to <br> in non-white-space:pre elements, detached
-PASS Newlines convert to <br> in <pre> element
-PASS Newlines convert to <br> in <pre> element, detached
-PASS Newlines convert to <br> in <textarea> element
-PASS Newlines convert to <br> in <textarea> element, detached
-PASS Newlines convert to <br> in white-space:pre element
-PASS Newlines convert to <br> in white-space:pre element, detached
-PASS CRs convert to <br> in non-white-space:pre elements
-PASS CRs convert to <br> in non-white-space:pre elements, detached
-PASS CRs convert to <br> in <pre> element
-PASS CRs convert to <br> in <pre> element, detached
-PASS Newline/CR pair converts to <br> in non-white-space:pre element
-PASS Newline/CR pair converts to <br> in non-white-space:pre element, detached
-PASS Newline/newline pair converts to two <br>s in non-white-space:pre element
-PASS Newline/newline pair converts to two <br>s in non-white-space:pre element, detached
-PASS CR/CR pair converts to two <br>s in non-white-space:pre element
-PASS CR/CR pair converts to two <br>s in non-white-space:pre element, detached
-PASS CRs convert to <br> in white-space:pre element
-PASS CRs convert to <br> in white-space:pre element, detached
-PASS < preserved
-PASS < preserved, detached
-PASS > preserved
-PASS > preserved, detached
-PASS & preserved
-PASS & preserved, detached
-PASS " preserved
-PASS " preserved, detached
-PASS ' preserved
-PASS ' preserved, detached
-PASS innerText not supported on SVG elements
-PASS innerText not supported on SVG elements, detached
-PASS innerText not supported on MathML elements
-PASS innerText not supported on MathML elements, detached
-PASS Null characters preserved
-PASS Null characters preserved, detached
-PASS Tabs preserved
-PASS Tabs preserved, detached
-PASS Leading whitespace preserved
-PASS Leading whitespace preserved, detached
-PASS Trailing whitespace preserved
-PASS Trailing whitespace preserved, detached
-PASS Whitespace not compressed
-PASS Whitespace not compressed, detached
-FAIL Existing text deleted assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-FAIL Existing text deleted, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS Existing <br> deleted
-PASS Existing <br> deleted, detached
-PASS Assigning the empty string
-PASS Assigning the empty string, detached
-PASS Assigning null
-PASS Assigning null, detached
-PASS Assigning undefined
-PASS Assigning undefined, detached
-PASS Start with CR
-PASS Start with CR, detached
-PASS Start with LF
-PASS Start with LF, detached
-PASS Start with CRLF
-PASS Start with CRLF, detached
-PASS End with CR
-PASS End with CR, detached
-PASS End with LF
-PASS End with LF, detached
-PASS End with CRLF
-PASS End with CRLF, detached
-PASS innerText on <area> element
-FAIL innerText on <area> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <base> element
-FAIL innerText on <base> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <basefont> element
-FAIL innerText on <basefont> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <bgsound> element
-FAIL innerText on <bgsound> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <br> element
-FAIL innerText on <br> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <col> element
-FAIL innerText on <col> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <embed> element
-FAIL innerText on <embed> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <frame> element
-FAIL innerText on <frame> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <hr> element
-FAIL innerText on <hr> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <image> element
-FAIL innerText on <image> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <img> element
-FAIL innerText on <img> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <input> element
-FAIL innerText on <input> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <keygen> element
-FAIL innerText on <keygen> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <link> element
-FAIL innerText on <link> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <menuitem> element
-FAIL innerText on <menuitem> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <meta> element
-FAIL innerText on <meta> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <param> element
-FAIL innerText on <param> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <source> element
-FAIL innerText on <source> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <track> element
-FAIL innerText on <track> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <wbr> element
-FAIL innerText on <wbr> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <colgroup> element
-FAIL innerText on <colgroup> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <frameset> element
-FAIL innerText on <frameset> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <head> element
-FAIL innerText on <head> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <html> element
-FAIL innerText on <html> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <table> element
-FAIL innerText on <table> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <tbody> element
-FAIL innerText on <tbody> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <tfoot> element
-FAIL innerText on <tfoot> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <thead> element
-FAIL innerText on <thead> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-PASS innerText on <tr> element
-FAIL innerText on <tr> element, detached assert_not_equals: Child should be a *new* text node got disallowed value Text node "abc"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/reflection-embedded-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/reflection-embedded-expected.txt
index d109dc5..aea251d9 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/reflection-embedded-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/dom/reflection-embedded-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 7087 tests; 6958 PASS, 129 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 7085 tests; 6956 PASS, 129 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS picture.title: 32 tests
 PASS picture.lang: 32 tests
 PASS picture.dir: 62 tests
@@ -20,8 +20,8 @@
 PASS img.crossOrigin: 52 tests
 PASS img.useMap: 32 tests
 PASS img.isMap: 33 tests
-PASS img.width: 9 tests
-PASS img.height: 9 tests
+PASS img.width: 8 tests
+PASS img.height: 8 tests
 PASS img.referrerPolicy: 27 tests
 FAIL img.referrerPolicy: setAttribute() to "same-origin" assert_equals: IDL get expected "same-origin" but got ""
 PASS img.referrerPolicy: 3 tests
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/reflection-forms-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/reflection-forms-expected.txt
deleted file mode 100644
index 6be164e..0000000
--- a/third_party/blink/web_tests/external/wpt/html/dom/reflection-forms-expected.txt
+++ /dev/null
@@ -1,195 +0,0 @@
-This is a testharness.js-based test.
-Found 6725 tests; 6722 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS form.title: 32 tests
-PASS form.lang: 32 tests
-PASS form.dir: 62 tests
-PASS form.className (<form class>): 32 tests
-PASS form.hidden: 33 tests
-PASS form.accessKey: 32 tests
-PASS form.tabIndex: 24 tests
-PASS form.acceptCharset (<form accept-charset>): 32 tests
-PASS form.action: 38 tests
-PASS form.autocomplete: 52 tests
-PASS form.enctype: 62 tests
-PASS form.encoding (<form enctype>): 62 tests
-PASS form.method: 62 tests
-PASS form.name: 32 tests
-PASS form.noValidate: 33 tests
-PASS form.target: 32 tests
-PASS fieldset.title: 32 tests
-PASS fieldset.lang: 32 tests
-PASS fieldset.dir: 62 tests
-PASS fieldset.className (<fieldset class>): 32 tests
-PASS fieldset.hidden: 33 tests
-PASS fieldset.accessKey: 32 tests
-PASS fieldset.tabIndex: 24 tests
-PASS fieldset.disabled: 33 tests
-PASS fieldset.name: 32 tests
-PASS legend.title: 32 tests
-PASS legend.lang: 32 tests
-PASS legend.dir: 62 tests
-PASS legend.className (<legend class>): 32 tests
-PASS legend.hidden: 33 tests
-PASS legend.accessKey: 32 tests
-PASS legend.tabIndex: 24 tests
-PASS legend.align: 32 tests
-PASS label.title: 32 tests
-PASS label.lang: 32 tests
-PASS label.dir: 62 tests
-PASS label.className (<label class>): 32 tests
-PASS label.hidden: 33 tests
-PASS label.accessKey: 32 tests
-PASS label.tabIndex: 24 tests
-PASS label.htmlFor (<label for>): 32 tests
-PASS input.title: 32 tests
-PASS input.lang: 32 tests
-PASS input.dir: 62 tests
-PASS input.className (<input class>): 32 tests
-PASS input.hidden: 33 tests
-PASS input.accessKey: 32 tests
-PASS input.tabIndex: 24 tests
-PASS input.accept: 32 tests
-PASS input.alt: 32 tests
-PASS input.autocomplete: 17 tests
-PASS input.autofocus: 33 tests
-PASS input.defaultChecked (<input checked>): 33 tests
-PASS input.dirName: 32 tests
-PASS input.disabled: 33 tests
-PASS input.formAction: 38 tests
-PASS input.formEnctype: 62 tests
-PASS input.formMethod: 52 tests
-PASS input.formNoValidate: 33 tests
-PASS input.formTarget: 32 tests
-PASS input.height: 9 tests
-PASS input.max: 32 tests
-PASS input.maxLength: 61 tests
-PASS input.min: 32 tests
-PASS input.minLength: 61 tests
-PASS input.multiple: 33 tests
-PASS input.name: 32 tests
-PASS input.pattern: 32 tests
-PASS input.placeholder: 32 tests
-PASS input.readOnly: 33 tests
-PASS input.required: 33 tests
-PASS input.size: 59 tests
-PASS input.src: 38 tests
-PASS input.step: 32 tests
-PASS input.type: 256 tests
-PASS input.width: 9 tests
-PASS input.defaultValue (<input value>): 32 tests
-PASS input.align: 32 tests
-PASS input.useMap: 32 tests
-PASS button.title: 32 tests
-PASS button.lang: 32 tests
-PASS button.dir: 62 tests
-PASS button.className (<button class>): 32 tests
-PASS button.hidden: 33 tests
-PASS button.accessKey: 32 tests
-PASS button.tabIndex: 24 tests
-PASS button.autofocus: 33 tests
-PASS button.disabled: 33 tests
-PASS button.formAction: 38 tests
-PASS button.formEnctype: 62 tests
-PASS button.formMethod: 62 tests
-PASS button.formNoValidate: 33 tests
-PASS button.formTarget: 32 tests
-PASS button.name: 32 tests
-PASS button.type: 62 tests
-PASS button.value: 32 tests
-PASS select.title: 32 tests
-PASS select.lang: 32 tests
-PASS select.dir: 62 tests
-PASS select.className (<select class>): 32 tests
-PASS select.hidden: 33 tests
-PASS select.accessKey: 32 tests
-PASS select.tabIndex: 24 tests
-PASS select.autocomplete: 17 tests
-PASS select.autofocus: 33 tests
-PASS select.disabled: 33 tests
-PASS select.multiple: 33 tests
-PASS select.name: 32 tests
-PASS select.required: 33 tests
-PASS select.size: 62 tests
-PASS datalist.title: 32 tests
-PASS datalist.lang: 32 tests
-PASS datalist.dir: 62 tests
-PASS datalist.className (<datalist class>): 32 tests
-PASS datalist.hidden: 33 tests
-PASS datalist.accessKey: 32 tests
-PASS datalist.tabIndex: 24 tests
-PASS optgroup.title: 32 tests
-PASS optgroup.lang: 32 tests
-PASS optgroup.dir: 62 tests
-PASS optgroup.className (<optgroup class>): 32 tests
-PASS optgroup.hidden: 33 tests
-PASS optgroup.accessKey: 32 tests
-PASS optgroup.tabIndex: 24 tests
-PASS optgroup.disabled: 33 tests
-PASS optgroup.label: 32 tests
-PASS option.title: 32 tests
-PASS option.lang: 32 tests
-PASS option.dir: 62 tests
-PASS option.className (<option class>): 32 tests
-PASS option.hidden: 33 tests
-PASS option.accessKey: 32 tests
-PASS option.tabIndex: 24 tests
-PASS option.disabled: 33 tests
-PASS option.label: 17 tests
-PASS option.defaultSelected (<option selected>): 33 tests
-PASS option.value: 17 tests
-PASS textarea.title: 32 tests
-PASS textarea.lang: 32 tests
-PASS textarea.dir: 62 tests
-PASS textarea.className (<textarea class>): 32 tests
-PASS textarea.hidden: 33 tests
-PASS textarea.accessKey: 32 tests
-PASS textarea.tabIndex: 24 tests
-PASS textarea.autocomplete: 17 tests
-PASS textarea.autofocus: 33 tests
-PASS textarea.cols: 59 tests
-PASS textarea.dirName: 32 tests
-PASS textarea.disabled: 33 tests
-PASS textarea.maxLength: 61 tests
-PASS textarea.minLength: 61 tests
-PASS textarea.name: 32 tests
-PASS textarea.placeholder: 32 tests
-PASS textarea.readOnly: 33 tests
-PASS textarea.required: 33 tests
-PASS textarea.rows: 59 tests
-PASS textarea.wrap: 32 tests
-PASS output.title: 32 tests
-PASS output.lang: 32 tests
-PASS output.dir: 62 tests
-PASS output.className (<output class>): 32 tests
-PASS output.hidden: 33 tests
-PASS output.accessKey: 32 tests
-PASS output.tabIndex: 24 tests
-PASS output.name: 32 tests
-PASS progress.title: 32 tests
-PASS progress.lang: 32 tests
-PASS progress.dir: 62 tests
-PASS progress.className (<progress class>): 32 tests
-PASS progress.hidden: 33 tests
-PASS progress.accessKey: 32 tests
-PASS progress.tabIndex: 24 tests
-PASS meter.title: 32 tests
-PASS meter.lang: 32 tests
-PASS meter.dir: 62 tests
-PASS meter.className (<meter class>): 32 tests
-PASS meter.hidden: 33 tests
-PASS meter.accessKey: 32 tests
-PASS meter.tabIndex: 24 tests
-PASS meter.value: 8 tests
-PASS meter.min: 8 tests
-PASS meter.max: typeof IDL attribute
-FAIL meter.max: IDL get with DOM attribute unset assert_equals: expected 0 but got 1
-PASS meter.max: 6 tests
-PASS meter.low: 8 tests
-PASS meter.high: typeof IDL attribute
-FAIL meter.high: IDL get with DOM attribute unset assert_equals: expected 0 but got 1
-PASS meter.high: 6 tests
-PASS meter.optimum: typeof IDL attribute
-FAIL meter.optimum: IDL get with DOM attribute unset assert_equals: expected 0 but got 0.5
-PASS meter.optimum: 6 tests
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/reflection-metadata-expected.txt b/third_party/blink/web_tests/external/wpt/html/dom/reflection-metadata-expected.txt
index 408ab6f..7cc499c 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/reflection-metadata-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/dom/reflection-metadata-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 2446 tests; 2365 PASS, 81 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 2445 tests; 2365 PASS, 80 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS head.title: 32 tests
 PASS head.lang: 32 tests
 PASS head.dir: 62 tests
@@ -21,9 +21,7 @@
 PASS base.hidden: 33 tests
 PASS base.accessKey: 32 tests
 PASS base.tabIndex: 24 tests
-PASS base.href: typeof IDL attribute
-FAIL base.href: IDL get with DOM attribute unset assert_equals: expected "" but got "http://web-platform.test:8001/html/dom/reflection-metadata.html"
-PASS base.href: 18 tests
+PASS base.href: 19 tests
 PASS base.target: 32 tests
 PASS link.title: 32 tests
 PASS link.lang: 32 tests
diff --git a/third_party/blink/web_tests/external/wpt/html/dom/reflection.js b/third_party/blink/web_tests/external/wpt/html/dom/reflection.js
index 9a98478..3f8d7c1e 100644
--- a/third_party/blink/web_tests/external/wpt/html/dom/reflection.js
+++ b/third_party/blink/web_tests/external/wpt/html/dom/reflection.js
@@ -636,7 +636,7 @@
         // Hard-coded special case
         defaultVal = domObj.ownerDocument.URL;
     }
-    if (defaultVal !== null || data.isNullable) {
+    if (!data.customGetter && (defaultVal !== null || data.isNullable)) {
         ReflectionHarness.test(function() {
             ReflectionHarness.assertEquals(idlObj[idlName], defaultVal);
         }, "IDL get with DOM attribute unset");
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resource-timing.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resource-timing.https.html
index 123bbc8..7fc3fcd 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resource-timing.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resource-timing.https.html
@@ -15,7 +15,7 @@
 function verify(options) {
     const url = options.mode === 'cross-origin' ? crossOriginUrl(options.resource)
                                       : resourceUrl(options.resource);
-    const entryList = options.performance.getEntriesByName(url);
+    const entryList = options.performance.getEntriesByName(url, 'resource');
     if (options.should_no_performance_entry) {
         // The performance timeline may not have an entry for a resource
         // which failed to load.
@@ -137,7 +137,7 @@
 
 test(() => {
     const url = resourceUrl('resources/test-helpers.sub.js');
-    const entry = window.performance.getEntriesByName(url)[0];
+    const entry = window.performance.getEntriesByName(url, 'resource')[0];
     assert_equals(entry.workerStart, 0, 'Non-controlled');
 }, 'Non-controlled resource loads');
 
diff --git a/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https-expected.txt b/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https-expected.txt
new file mode 100644
index 0000000..104d42a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL NFCReader.start should fail if NFC HW is disabled. promise_test: Unhandled rejection with value: object "ReferenceError: NFCReader is not defined"
+FAIL NFCWriter.push should fail when NFC HW is disabled. NFCWriter is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https.html b/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https.html
index cd84961c..bb2cd42 100644
--- a/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https.html
+++ b/third_party/blink/web_tests/external/wpt/web-nfc/nfc_hw_disabled-manual.https.html
@@ -11,7 +11,7 @@
 <h2>Precondition</h2>
 <ol>
   <li>
-    Disable the NFC Module.
+    Disable the NFC module or run test on a device without NFC module.
   </li>
 </ol>
 
@@ -21,12 +21,17 @@
 
 "use strict";
 
-promise_test(t => {
- return promise_rejects(t, 'NotSupportedError', navigator.nfc.watch(noop));
-}, "Test that nfc.watch fails if NFC HW is disabled.");
+promise_test(async t => {
+  const reader = new NFCReader();
+  const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
+  reader.start();
+  const event = await readerWatcher.wait_for("error");
+  assert_equals(event.error.name, 'NotReadableError');
+}, "NFCReader.start should fail if NFC HW is disabled.");
 
 promise_test(t => {
-  return promise_rejects(t, 'NotSupportedError', navigator.nfc.push(test_text_data));
-}, "nfc.push should fail when NFC HW is disabled.");
+  const writer = new NFCWriter();
+  return promise_rejects(t, 'NotReadableError', writer.push(test_text_data));
+}, "NFCWriter.push should fail when NFC HW is disabled.");
 
 </script>
diff --git a/third_party/blink/web_tests/fast/dom/HTMLElement/set-inner-outer-optimization.html b/third_party/blink/web_tests/fast/dom/HTMLElement/set-inner-outer-optimization.html
index 3c774041..016c0833 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLElement/set-inner-outer-optimization.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLElement/set-inner-outer-optimization.html
@@ -99,8 +99,8 @@
     runTest('', 'innerText', '<a></a><b></b>', 'modified');
 
     runTest('text', 'innerText', '', 'modified');
-    runTest('text', 'innerText', 'different text', 'modified, with same first child');
-    runTest('text', 'innerText', 'text', 'modified, with same first child');
+    runTest('text', 'innerText', 'different text', 'modified');
+    runTest('text', 'innerText', 'text', 'modified');
 
     runTest('<a></a>', 'innerText', '', 'modified');
     runTest('<a></a>', 'innerText', 'text', 'modified');
diff --git a/third_party/blink/web_tests/paint/invalidation/block-no-inflow-children.html b/third_party/blink/web_tests/paint/invalidation/block-no-inflow-children.html
index 3be22ea..b604afe 100644
--- a/third_party/blink/web_tests/paint/invalidation/block-no-inflow-children.html
+++ b/third_party/blink/web_tests/paint/invalidation/block-no-inflow-children.html
@@ -2,7 +2,7 @@
     <script>
         function repaintTest()
         {
-            document.getElementById("target").innerText += " la";
+            document.getElementById("target").firstChild.data += " la";
         }
     </script>
     <script src="resources/text-based-repaint.js"></script>
diff --git a/third_party/blink/web_tests/paint/invalidation/position/layout-state-relative.html b/third_party/blink/web_tests/paint/invalidation/position/layout-state-relative.html
index 284d5e1..16419ae2 100644
--- a/third_party/blink/web_tests/paint/invalidation/position/layout-state-relative.html
+++ b/third_party/blink/web_tests/paint/invalidation/position/layout-state-relative.html
@@ -6,7 +6,7 @@
     <script type="text/javascript">
        function repaintTest()
        {
-           document.getElementById("target").innerText = "PASS";
+           document.getElementById("target").firstChild.data = "PASS";
        }
     </script>
 </head>
diff --git a/third_party/blink/web_tests/paint/invalidation/reflection/reflection-with-rotation.html b/third_party/blink/web_tests/paint/invalidation/reflection/reflection-with-rotation.html
index fa2810c..5840c5e1 100644
--- a/third_party/blink/web_tests/paint/invalidation/reflection/reflection-with-rotation.html
+++ b/third_party/blink/web_tests/paint/invalidation/reflection/reflection-with-rotation.html
@@ -6,7 +6,7 @@
     <script type="text/javascript">
        function repaintTest()
        {
-           document.getElementById("target").innerText = "PASS";
+           document.getElementById("target").firstChild.data = "PASS";
        }
     </script>
     <style type="text/css" media="screen">
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset.html b/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset.html
index 26d1094..326ea3c 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset.html
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset.html
@@ -16,7 +16,7 @@
   parent.scrollLeft = 10;
 
   function repaintTest() {
-    document.getElementById('target').innerText = 'after';
+    document.getElementById('target').firstChild.data = 'after';
   }
 </script>
 </html>
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset2.html b/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset2.html
index 30b3aa4..ed10369 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset2.html
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset2.html
@@ -22,7 +22,7 @@
   parent2.scrollLeft = 7;
 
   function repaintTest() {
-    document.getElementById('target').innerText = 'after'
+    document.getElementById('target').firstChild.data = 'after'
   }
 </script>
 </html>
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset3.html b/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset3.html
index 242cb67..7d92c1d2 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset3.html
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/layout-state-scrolloffset3.html
@@ -16,7 +16,7 @@
   parent.scrollLeft = 10;
 
   function repaintTest() {
-    document.getElementById('target').innerText = 'after';
+    document.getElementById('target').firstChild.data = 'after';
   }
 </script>
 </html>
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/line-in-scrolled-clipped-block.html b/third_party/blink/web_tests/paint/invalidation/scroll/line-in-scrolled-clipped-block.html
index 60d6714..32223e8 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/line-in-scrolled-clipped-block.html
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/line-in-scrolled-clipped-block.html
@@ -7,6 +7,6 @@
 
     function repaintTest()
     {
-        target.innerText = "                         PASS     .";
+        target.firstChild.data = "                         PASS     .";
     }
 </script>
diff --git a/third_party/blink/web_tests/paint/invalidation/transform/transform-layout-repaint.html b/third_party/blink/web_tests/paint/invalidation/transform/transform-layout-repaint.html
index 174d17a..dbee7284 100644
--- a/third_party/blink/web_tests/paint/invalidation/transform/transform-layout-repaint.html
+++ b/third_party/blink/web_tests/paint/invalidation/transform/transform-layout-repaint.html
@@ -6,7 +6,7 @@
     <script type="text/javascript">
        function repaintTest()
        {
-           document.getElementById("target").innerText = "PASS";
+           document.getElementById("target").firstChild.data = "PASS";
        }
     </script>
     <style type="text/css" media="screen">
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 8594e74..6cb1c315 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5285,7 +5285,9 @@
     setter onresourcetimingbufferfull
 interface PerformanceElementTiming : PerformanceEntry
     attribute @@toStringTag
+    getter identifier
     getter intersectionRect
+    getter responseEnd
     method constructor
     method toJSON
 interface PerformanceEntry
diff --git a/third_party/freetype/BUILD.gn b/third_party/freetype/BUILD.gn
index 4046a4b..0dcf7742 100644
--- a/third_party/freetype/BUILD.gn
+++ b/third_party/freetype/BUILD.gn
@@ -61,6 +61,10 @@
   defines = []
   include_dirs = []
 
+  # FreeType only exposes ft2build.h, all other FreeType headers are accessed by macro names.
+  # gn check does not expand macros, so list only this header.
+  public = [ "src/include/ft2build.h" ]
+
   sources = [
     "include/freetype-custom-config/ftconfig.h",
     "include/freetype-custom-config/ftmodule.h",
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 318973e6..a0eeb6c2 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-9-1-349
-Revision: 6d65c60fca0ebce88e2bcfeac92a7a791e03bf42
+Version: VER-2-9-1-351
+Revision: 31757f969fba60d75404f31e8f1168bef5011770
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
 License File: src/docs/FTL.TXT
diff --git a/third_party/ink/README.chromium b/third_party/ink/README.chromium
index 314199e..405ce3c 100644
--- a/third_party/ink/README.chromium
+++ b/third_party/ink/README.chromium
@@ -1,7 +1,7 @@
 Name: Google Ink
 Short Name: ink
 URL: https://github.com/google/ink
-Version: 233314166
+Version: 236729529
 License: Apache 2.0
 Security Critical: yes
 
diff --git a/third_party/ink/build/ink_lib_binary.js.sha1 b/third_party/ink/build/ink_lib_binary.js.sha1
index 55ca656..d9f90f8 100644
--- a/third_party/ink/build/ink_lib_binary.js.sha1
+++ b/third_party/ink/build/ink_lib_binary.js.sha1
@@ -1 +1 @@
-36d392d7bec3d8550e40a947c0db29b40e19d60b
\ No newline at end of file
+b64c8e4dbec4b01a3aa0cd4a0b7fd37ff735fb72
\ No newline at end of file
diff --git a/third_party/ink/build/ink_lib_externs.js.sha1 b/third_party/ink/build/ink_lib_externs.js.sha1
index 26f1cfff..f68aa91 100644
--- a/third_party/ink/build/ink_lib_externs.js.sha1
+++ b/third_party/ink/build/ink_lib_externs.js.sha1
@@ -1 +1 @@
-b866a5dbc00ee771cf56ed08cc78015cef160758
\ No newline at end of file
+90f553cbf55aa906cdd43c4b27dc3d5ef9e75ebb
\ No newline at end of file
diff --git a/third_party/ink/build/wasm-threads/glcore_base.js.mem.sha1 b/third_party/ink/build/wasm-threads/glcore_base.js.mem.sha1
new file mode 100644
index 0000000..6aaf19f
--- /dev/null
+++ b/third_party/ink/build/wasm-threads/glcore_base.js.mem.sha1
@@ -0,0 +1 @@
+d346855d34df6a757683494be06cad9aac8e1a01
\ No newline at end of file
diff --git a/third_party/ink/build/wasm-threads/glcore_base.js.sha1 b/third_party/ink/build/wasm-threads/glcore_base.js.sha1
new file mode 100644
index 0000000..cb76fe4
--- /dev/null
+++ b/third_party/ink/build/wasm-threads/glcore_base.js.sha1
@@ -0,0 +1 @@
+4d8f1d787a8c0f496b733c7578824cf6e0608840
\ No newline at end of file
diff --git a/third_party/ink/build/wasm-threads/glcore_base.wasm.sha1 b/third_party/ink/build/wasm-threads/glcore_base.wasm.sha1
new file mode 100644
index 0000000..714e08a
--- /dev/null
+++ b/third_party/ink/build/wasm-threads/glcore_base.wasm.sha1
@@ -0,0 +1 @@
+3e0eaa76ec1238f9cd1cecf7808d700cd9a84924
\ No newline at end of file
diff --git a/third_party/ink/build/wasm-threads/glcore_wasm_bootstrap_compiled.js.sha1 b/third_party/ink/build/wasm-threads/glcore_wasm_bootstrap_compiled.js.sha1
new file mode 100644
index 0000000..67c5116
--- /dev/null
+++ b/third_party/ink/build/wasm-threads/glcore_wasm_bootstrap_compiled.js.sha1
@@ -0,0 +1 @@
+4ab63d6214d411e2b096d5d0f3a072d0c9332201
\ No newline at end of file
diff --git a/third_party/ink/build/wasm-threads/pthread-main.js.sha1 b/third_party/ink/build/wasm-threads/pthread-main.js.sha1
new file mode 100644
index 0000000..1e5ae02a
--- /dev/null
+++ b/third_party/ink/build/wasm-threads/pthread-main.js.sha1
@@ -0,0 +1 @@
+89f47a099c85e412a4dc1755c8f01deef42e4713
\ No newline at end of file
diff --git a/third_party/ink/build/wasm/glcore_base.js.sha1 b/third_party/ink/build/wasm/glcore_base.js.sha1
index dbc150f2..8c62ba4 100644
--- a/third_party/ink/build/wasm/glcore_base.js.sha1
+++ b/third_party/ink/build/wasm/glcore_base.js.sha1
@@ -1 +1 @@
-529bc90cbda84c1c1ea684f3b74203932e6897da
\ No newline at end of file
+c8b78d6f7b0b9639cf05f68100568f54024bd621
\ No newline at end of file
diff --git a/third_party/ink/build/wasm/glcore_base.wasm.sha1 b/third_party/ink/build/wasm/glcore_base.wasm.sha1
index 9278463..68dc8071 100644
--- a/third_party/ink/build/wasm/glcore_base.wasm.sha1
+++ b/third_party/ink/build/wasm/glcore_base.wasm.sha1
@@ -1 +1 @@
-a27172fe2f09c9c5c194219754beb0e7433f6264
\ No newline at end of file
+5310afd49946994abbf109c77d06831143d7f623
\ No newline at end of file
diff --git a/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1 b/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1
index 5a7db281..4d72bb83 100644
--- a/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1
+++ b/third_party/ink/build/wasm/glcore_wasm_bootstrap_compiled.js.sha1
@@ -1 +1 @@
-c742f9bc02eeb6189d07d7acb893af6ec7c0f49e
\ No newline at end of file
+49ed90d6ccc69b214c88fe99c43700099ef00bdb
\ No newline at end of file
diff --git a/tools/metrics/histograms/OWNERS b/tools/metrics/histograms/OWNERS
index 98dfc58d..70ef08f 100644
--- a/tools/metrics/histograms/OWNERS
+++ b/tools/metrics/histograms/OWNERS
@@ -8,6 +8,7 @@
 per-file histograms.xml=csharrison@chromium.org
 per-file histograms.xml=cthomp@chromium.org  # For security-related metrics only.
 per-file histograms.xml=schenney@chromium.org
+per-file histograms.xml=tbansal@chromium.org
 
 per-file histograms.xml=set noparent
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4c5e61c..06f8fa3d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -31082,6 +31082,7 @@
   <int value="-1869845022" label="force-show-update-menu-item"/>
   <int value="-1869432243" label="GoogleBrandedContextMenu:disabled"/>
   <int value="-1868978829" label="spurious-power-button-accel-count"/>
+  <int value="-1868957045" label="SyncUSSAutofillWalletMetadata:enabled"/>
   <int value="-1868284723" label="DirectManipulationStylus:disabled"/>
   <int value="-1867382602" label="WebRTC-H264WithOpenH264FFmpeg:enabled"/>
   <int value="-1867342522" label="MaterialDesignHistory:enabled"/>
@@ -31636,6 +31637,7 @@
       label="OmniboxSpeculativeServiceWorkerStartOnQueryInput:disabled"/>
   <int value="-989671895" label="OfflineIndicatorAlwaysHttpProbe:enabled"/>
   <int value="-984052166" label="DoodlesOnLocalNtp:enabled"/>
+  <int value="-981237342" label="SyncUSSAutofillWalletMetadata:disabled"/>
   <int value="-980260493" label="NTPSnippets:disabled"/>
   <int value="-979313250" label="enable-google-branded-context-menu"/>
   <int value="-979057409" label="enable-seccomp-filter-sandbox"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7fd239bd..1c518a28 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -78377,6 +78377,119 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from the user input that triggers the top-level navigation for an
+    AMP document to the time that the navigation for the AMP document is
+    initiated in the AMP frame. This gives insight into how often AMP documents
+    are navigated to via same document navigations without being prerendered.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.Experimental.PageTiming.MainFrameToSubFrameNavigationDelta.Subframe"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from the main frame navigation to the subsequent subframe
+    navigation for the AMP document. Only recorded for non-same-page
+    navigations.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from navigation in the AMP frame to the user input that triggers
+    the top-level navigation for that AMP document. This gives insight into the
+    delta between when an AMP frame is prerendered and when it is actually
+    displayed for same document navigations.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay3.Subframe"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    Measures First Input Delay, the duration between the hardware timestamp and
+    the start of event processing on the main thread for the first meaningful
+    input per navigation, in an AMP subframe document. Recorded on first page
+    interaction. See https://goo.gl/tr1oTZ for a detailed explanation. Excludes
+    scrolls. Only same-document navigations are included.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay3.Subframe.FullNavigation"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    Measures First Input Delay, the duration between the hardware timestamp and
+    the start of event processing on the main thread for the first meaningful
+    input per navigation, in an AMP subframe document. Recorded on first page
+    interaction. See https://goo.gl/tr1oTZ for a detailed explanation. Excludes
+    scrolls. Only non-same-document navigations are included.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from user input to first &quot;contentful&quot; paint in an AMP
+    subframe document. Will be zero or near-zero in cases where the AMP subframe
+    document was prerendered. Only same-document navigations are included.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe.FullNavigation"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from user input to first &quot;contentful&quot; paint in an AMP
+    subframe document. Only non-same-document navigations are included.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.PaintTiming.InputToLargestContentPaint.Subframe"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from user input to largest &quot;contentful&quot; paint in an AMP
+    subframe document. Only same-document navigations are included.
+
+    Excludes any content painted after user input. The value is recorded at the
+    end of each page load unless there is an abort or user input before text or
+    image paint. See http://bit.ly/fcp_plus_plus for details.
+
+    Will be zero or near-zero in cases where the AMP subframe document was
+    prerendered.
+  </summary>
+</histogram>
+
+<histogram
+    name="PageLoad.Clients.AMP.PaintTiming.InputToLargestContentPaint.Subframe.FullNavigation"
+    units="ms">
+  <owner>bmcquade@chromium.org</owner>
+  <summary>
+    The time from user input to largest &quot;contentful&quot; paint in an AMP
+    subframe document. Only non-same-document navigations are included.
+
+    Excludes any content painted after user input. The value is recorded at the
+    end of each page load unless there is an abort or user input before text or
+    image paint. See http://bit.ly/fcp_plus_plus for details.
+  </summary>
+</histogram>
+
 <histogram name="PageLoad.Clients.AMP.SameDocumentView"
     enum="PageLoadMetricsAMPViewType">
   <owner>bmcquade@chromium.org</owner>
diff --git a/tools/perf/cli_tools/update_wpr/update_wpr.py b/tools/perf/cli_tools/update_wpr/update_wpr.py
index 6727857..2f8d00e 100644
--- a/tools/perf/cli_tools/update_wpr/update_wpr.py
+++ b/tools/perf/cli_tools/update_wpr/update_wpr.py
@@ -9,10 +9,13 @@
 import datetime
 import json
 import os
+import random
 import re
 import shutil
 import subprocess
 import tempfile
+import time
+import webbrowser
 
 from core import cli_helpers
 from core import path_util
@@ -24,7 +27,7 @@
 
 
 SRC_ROOT = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+    os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))
 RESULTS2JSON = os.path.join(
     SRC_ROOT, 'third_party', 'catapult', 'tracing', 'bin', 'results2json')
 HISTOGRAM2CSV = os.path.join(
@@ -35,6 +38,41 @@
 DEFAULT_REVIEWERS = ['perezju@chromium.org']
 
 
+def _GetBranchName():
+  return subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
+
+
+def _OpenBrowser(url):
+  # Redirect I/O before invoking browser to avoid it spamming our output.
+  # Based on https://stackoverflow.com/a/2323563.
+  savout = os.dup(1)
+  saverr = os.dup(2)
+  os.close(1)
+  os.close(2)
+  os.open(os.devnull, os.O_RDWR)
+  try:
+    webbrowser.open(url)
+  finally:
+    os.dup2(savout, 1)
+    os.dup2(saverr, 2)
+
+
+def _SendCLForReview(comment):
+  subprocess.check_call(
+      ['git', 'cl', 'comments', '--publish', '--add-comment', comment])
+
+
+def _EnsureEditor():
+  if 'EDITOR' not in os.environ:
+    os.environ['EDITOR'] = cli_helpers.Prompt(
+        'Looks like EDITOR environment varible is not defined. Please enter '
+        'the command to view logs: ')
+
+
+def _OpenEditor(filepath):
+  subprocess.check_call([os.environ['EDITOR'], filepath])
+
+
 class WprUpdater(object):
   def __init__(self, args):
     self.story = args.story
@@ -226,6 +264,48 @@
     with open(output_file, 'r') as output_fd:
       return json.load(output_fd)['issue_url']
 
+  def _CreateBranch(self):
+    sanitized_story = re.sub(r'[^A-Za-z0-9-_.]', r'-', self.story)
+    subprocess.check_call([
+      'git', 'new-branch',
+      'update-wpr-%s-%d' % (sanitized_story, random.randint(0, 10000)),
+    ])
+
+  def _FilterLogForDiff(self, log_filename):
+    """Removes unimportant details from console logs for cleaner diffs.
+
+    For example, log line from file `log_filename`
+
+      2018-02-01 22:23:22,123 operation abcdef01-abcd-abcd-0123-abcdef012345
+      from /tmp/tmpX34v/results.html took 22145ms when accessed via
+      https://127.0.0.1:1233/endpoint
+
+    would become
+
+      <timestamp> operation <guid> from /tmp/tmp<random>/results.html took
+      <duration> when accessed via https://127.0.0.1:<port>/endpoint
+
+    Returns:
+      Path to the filtered log.
+    """
+    with open(log_filename) as src, tempfile.NamedTemporaryFile(
+        suffix='diff', dir=self.output_dir, delete=False) as dest:
+      for line in src:
+        # Remove timestamps.
+        line = re.sub(
+            r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}', r'<timestamp>', line)
+        # Remove GUIDs.
+        line = re.sub(
+            r'[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}', r'<guid>', line)
+        # Remove random letters in paths to temp dirs and files.
+        line = re.sub(r'(/tmp/tmp)[^/\s]+', r'\1<random>', line)
+        # Remove random port in localhost URLs.
+        line = re.sub(r'(://127.0.0.1:)\d+', r'\1<port>', line)
+        # Remove random durations in ms.
+        line = re.sub(r'\d+ ms', r'<duration>', line)
+        dest.write(line)
+        return dest.name
+
   def _StartPinpointJob(self, configuration):
     """Creates, starts a Pinpoint job and returns its URL."""
     try:
@@ -328,6 +408,153 @@
         job_urls.append(job_url)
     return job_urls, failed_configs
 
+  def AutoRun(self):
+    # Let the quest begin...
+    cli_helpers.Comment(
+        'This script will help you update the recording of a story. It will go '
+        'through the following stages, which you can also invoke manually via '
+        'subcommand specified in parentheses:')
+    cli_helpers.Comment('  - help create a new branch if needed')
+    cli_helpers.Comment('  - run story with live network connection (live)')
+    cli_helpers.Comment('  - record story (record)')
+    cli_helpers.Comment('  - replay the recording (replay)')
+    cli_helpers.Comment('  - upload the recording to Google Storage (upload)')
+    cli_helpers.Comment(
+        '  - upload CL with updated recording reference (review)')
+    cli_helpers.Comment('  - trigger pinpoint tryjobs (pinpoint)')
+    cli_helpers.Comment('  - post links to these jobs on the CL')
+    cli_helpers.Comment(
+        'Note that you can always enter prefix of the answer to any of the '
+        'questions asked below, e.g. "y" for "yes" or "j" for "just-replay".')
+
+    # TODO(sergiyb): Detect if benchmark is not implemented and try to add it
+    # automatically by copying the same benchmark without :<current-year> suffix
+    # and changing name of the test, name of the benchmark and the year tag.
+
+    # Create branch if needed.
+    reuse_cl = False
+    branch = _GetBranchName()
+    if branch == 'HEAD':
+      cli_helpers.Comment('You are not on a branch.')
+      if not cli_helpers.Ask(
+          'Should script create a new branch automatically?'):
+        cli_helpers.Comment(
+            'Please create a new branch and start this script again')
+        return
+      self._CreateBranch()
+    else:
+      issue = self._GetBranchIssueUrl()
+      if issue is not None:
+        issue_message = 'with an associated issue: %s' % issue
+      else:
+        issue_message = 'without an associated issue'
+      cli_helpers.Comment(
+          'You are on a branch {branch} {issue_message}. Please commit or '
+          'stash any changes unrelated to the updated story before '
+          'proceeding.', branch=branch, issue_message=issue_message)
+      ans = cli_helpers.Ask(
+          'Should the script create a new branch automatically, reuse '
+          'existing one or exit?', answers=['create', 'reuse', 'exit'],
+          default='create')
+      if ans == 'create':
+        self._CreateBranch()
+      elif ans == 'reuse':
+        reuse_cl = issue is not None
+      elif ans == 'exit':
+        return
+
+    # Live run.
+    live_out_file = self.LiveRun()
+    cli_helpers.Comment(
+        'Please inspect the live run results above for any errors.')
+    ans = None
+    while ans != 'continue':
+      ans = cli_helpers.Ask(
+          'Should I continue with recording, view metric results in a browser, '
+          'view stdout/stderr output or stop?',
+          ['continue', 'metrics', 'output', 'stop'], default='continue')
+      if ans == 'stop':
+        cli_helpers.Comment(
+            'Please update the story class to resolve the observed issues and '
+            'then run this script again.')
+        return
+      elif ans == 'metrics':
+        _OpenBrowser('file://%s.results.html' % live_out_file)
+      elif ans == 'output':
+        _OpenEditor(live_out_file)
+
+    # Record & replay.
+    action = 'record'
+    while action != 'continue':
+      if action == 'record':
+        self.RecordWpr()
+      if action in ['record', 'just-replay']:
+        replay_out_file = self.ReplayWpr()
+        cli_helpers.Comment(
+            'Check that the console:error:all metrics above have low values '
+            'and are similar to the live run above.')
+      if action == 'diff':
+        diff_path = os.path.join(self.output_dir, 'live_replay.diff')
+        with open(diff_path, 'w') as diff_file:
+          subprocess.call([
+            'diff', '--color', self._FilterLogForDiff(live_out_file),
+            self._FilterLogForDiff(replay_out_file)], stdout=diff_file)
+        _OpenEditor(diff_path)
+      if action == 'stop':
+        return
+      action = cli_helpers.Ask(
+          'Should I record and replay again, just replay, continue with '
+          'uploading CL, stop and exit, or would you prefer to see diff '
+          'between live/replay console logs?',
+          ['record', 'just-replay', 'continue', 'stop', 'diff'],
+          default='continue')
+
+    # Upload WPR and create a WIP CL for the new story.
+    if not self.UploadWpr():
+      return
+    while self.UploadCL(short_description=reuse_cl) != 0:
+      if not cli_helpers.Ask('Upload failed. Should I try again?'):
+        return
+
+    # Gerrit needs some time to sync its backends, hence we sleep here for 5
+    # seconds. Otherwise, pinpoint app may get an answer that the CL that we've
+    # just uploaded does not exist yet.
+    cli_helpers.Comment(
+        'Waiting 20 seconds for the Gerrit backends to sync, so that Pinpoint '
+        'app can detect the newly-created CL.')
+    time.sleep(20)
+
+    # Trigger pinpoint jobs.
+    configs_to_trigger = None
+    job_urls = []
+    while True:
+      new_job_urls, configs_to_trigger = self.StartPinpointJobs(
+          configs_to_trigger)
+      job_urls.extend(new_job_urls)
+      if not configs_to_trigger or not cli_helpers.Ask(
+          'Do you want to try triggering the failed configs again?'):
+        break
+
+    if configs_to_trigger:
+      if not cli_helpers.Ask(
+          'Some jobs failed to trigger. Do you still want to send created '
+          'CL for review?', default='no'):
+        return
+
+    # Post a link to the triggered jobs, publish CL for review and open it.
+    _SendCLForReview(
+        'Started the following Pinpoint jobs:\n%s' %
+        '\n'.join('  - %s' % url for url in job_urls))
+    cli_helpers.Comment(
+        'Posted a message with Pinpoint job URLs on the CL and sent it for '
+        'review. Opening the CL in a browser...')
+    _OpenBrowser(self._GetBranchIssueUrl())
+
+    # Hooray, you won! :-)
+    cli_helpers.Comment(
+        'Thank you, you have successfully updated the recording for %s. Please '
+        'wait for LGTM and land the created CL.' % self.story)
+
 
 def Main(argv):
   parser = argparse.ArgumentParser()
@@ -351,15 +578,27 @@
       '--binary', default=None,
       help='Path to the Chromium/Chrome binary relative to output directory. '
            'Defaults to default Chrome browser installed if not specified.')
-  parser.add_argument(
-      'command', choices=[
-        'live', 'record', 'replay', 'upload', 'review', 'pinpoint'],
-      help='Mode in which to run this script.')
+
+  subparsers = parser.add_subparsers(
+      title='Mode in which to run this script', dest='command')
+  subparsers.add_parser(
+      'auto', help='interactive mode automating updating a recording')
+  subparsers.add_parser('live', help='run story on a live website')
+  subparsers.add_parser('record', help='record story from a live website')
+  subparsers.add_parser('replay', help='replay story from the recording')
+  subparsers.add_parser('upload', help='upload recording to the Google Storage')
+  subparsers.add_parser('review', help='create a CL with updated recording')
+  subparsers.add_parser(
+      'pinpoint', help='trigger Pinpoint jobs to test the recording')
 
   args = parser.parse_args(argv)
 
   updater = WprUpdater(args)
-  if args.command =='live':
+  if args.command == 'auto':
+    _EnsureEditor()
+    luci_auth.CheckLoggedIn()
+    updater.AutoRun()
+  elif args.command =='live':
     updater.LiveRun()
   elif args.command == 'record':
     updater.RecordWpr()
diff --git a/tools/perf/cli_tools/update_wpr/update_wpr_unittest.py b/tools/perf/cli_tools/update_wpr/update_wpr_unittest.py
index 934d7ed..65a82bf 100644
--- a/tools/perf/cli_tools/update_wpr/update_wpr_unittest.py
+++ b/tools/perf/cli_tools/update_wpr/update_wpr_unittest.py
@@ -35,6 +35,7 @@
     datetime.now.return_value.strftime.return_value = '<tstamp>'
 
     mock.patch('tempfile.mkdtemp', return_value='/tmp/dir').start()
+    mock.patch('random.randint', return_value=1234).start()
     mock.patch('core.cli_helpers.Fatal').start()
     mock.patch('core.cli_helpers.Error').start()
     mock.patch('core.cli_helpers.Step').start()
@@ -47,6 +48,7 @@
     mock.patch(WPR_UPDATER + 'RECORD_WPR', '.../record_wpr').start()
     mock.patch('os.path.join', lambda *parts: '/'.join(parts)).start()
     mock.patch('os.path.exists', return_value=True).start()
+    mock.patch('time.sleep').start()
 
     self.wpr_updater = update_wpr.WprUpdater(argparse.Namespace(
       story='<story>', device_id=None, repeat=1, binary=None, bug_id=None,
@@ -58,13 +60,13 @@
   def testMain(self):
     wpr_updater_cls = mock.patch(WPR_UPDATER + 'WprUpdater').start()
     update_wpr.Main([
-      'live',
       '-s', 'foo:bar:story:2019',
       '-d', 'H2345234FC33',
       '--binary', '<binary>',
       '-b', '1234',
       '-r', 'test_user1@chromium.org',
       '-r', 'test_user2@chromium.org',
+      'live',
     ])
     self.assertListEqual(wpr_updater_cls.mock_calls, [
       mock.call(argparse.Namespace(
@@ -90,6 +92,83 @@
     self.wpr_updater.Cleanup()
     rmtree.assert_called_once_with('/tmp/dir', ignore_errors=True)
 
+  def testGetBranchName(self):
+    self._check_output.return_value = 'master'
+    self.assertEqual(update_wpr._GetBranchName(), 'master')
+    self._check_output.assert_called_once_with(
+        ['git', 'rev-parse', '--abbrev-ref', 'HEAD'])
+
+  def testCreateBranch(self):
+    self.wpr_updater._CreateBranch()
+    self._check_call.assert_called_once_with(
+        ['git', 'new-branch', 'update-wpr--story--1234'])
+
+  def testSendCLForReview(self):
+    update_wpr._SendCLForReview('comment')
+    self._check_call.assert_called_once_with(
+        ['git', 'cl', 'comments', '--publish', '--add-comment', 'comment'])
+
+  @mock.patch('os.dup')
+  @mock.patch('os.close')
+  @mock.patch('os.dup2')
+  @mock.patch('webbrowser.open')
+  def testOpenBrowser(self, webbrowser_open, os_dup2, os_close, os_dup):
+    del os_dup2, os_close, os_dup  # unused
+    update_wpr._OpenBrowser('<url>')
+    webbrowser_open.assert_called_once_with('<url>')
+
+  def testAutoRun(self):
+    # Mock low-level methods tested above.
+    mock.patch(WPR_UPDATER + '_GetBranchName', return_value='HEAD').start()
+    mock.patch(
+        WPR_UPDATER + 'WprUpdater._GetBranchIssueUrl',
+        return_value='<issue-url>').start()
+    mock.patch(WPR_UPDATER + 'WprUpdater._CreateBranch').start()
+    send_cl_for_review = mock.patch(WPR_UPDATER + '_SendCLForReview').start()
+    open_browser = mock.patch(WPR_UPDATER + '_OpenBrowser').start()
+
+    # Mock high-level methods tested below.
+    live_run = mock.patch(WPR_UPDATER + 'WprUpdater.LiveRun').start()
+    record_wpr = mock.patch(WPR_UPDATER + 'WprUpdater.RecordWpr').start()
+    replay_wpr = mock.patch(WPR_UPDATER + 'WprUpdater.ReplayWpr').start()
+    upload_wpr = mock.patch(
+        WPR_UPDATER + 'WprUpdater.UploadWpr', return_value=True).start()
+    upload_cl = mock.patch(
+        WPR_UPDATER + 'WprUpdater.UploadCL', return_value=0).start()
+    start_pinpoint_jobs = mock.patch(
+        WPR_UPDATER + 'WprUpdater.StartPinpointJobs',
+        return_value=(['<url1>', '<url2>', '<url3>'], [])).start()
+
+    # Mock user interaction.
+    mock.patch('core.cli_helpers.Ask', side_effect=[
+        True,        # Should script create a new branch automatically?
+        'continue',  # Should I continue with recording, ...?
+        'continue',  # Should I record and replay again, ...?
+    ]).start()
+
+    self.wpr_updater.AutoRun()
+
+    # Run once to make sure story works.
+    live_run.assert_called_once_with()
+    # Run again to create a recording.
+    record_wpr.assert_called_once_with()
+    # Replay to verify the recording.
+    replay_wpr.assert_called_once_with()
+    # Upload the recording.
+    upload_wpr.assert_called_once_with()
+    # Upload the CL.
+    upload_cl.assert_called_once_with(short_description=False)
+    # Start pinpoint jobs to verify recording works on the bots.
+    start_pinpoint_jobs.assert_called_once_with(None)
+    # Send CL for review with a comment listing triggered Pinpoint jobs.
+    send_cl_for_review.assert_called_once_with(
+        'Started the following Pinpoint jobs:\n'
+        '  - <url1>\n'
+        '  - <url2>\n'
+        '  - <url3>')
+    # Open the CL in browser,
+    open_browser.assert_called_once_with('<issue-url>')
+
   def testLiveRun(self):
     run_benchmark = mock.patch(
         WPR_UPDATER + 'WprUpdater._RunSystemHealthMemoryBenchmark',
diff --git a/tools/perf/core/cli_helpers.py b/tools/perf/core/cli_helpers.py
index 54d2cdd..b184c51 100644
--- a/tools/perf/core/cli_helpers.py
+++ b/tools/perf/core/cli_helpers.py
@@ -137,6 +137,15 @@
         ', '.join(choices[:-1]), choices[-1]))
 
 
+def Prompt(question, accept_empty=False):
+  while True:
+    print(Colored(question, color='cyan'))
+    answer = raw_input().strip()
+    if answer or accept_empty:
+      return answer
+    Error('Please enter non-empty answer')
+
+
 def CheckLog(command, log_path, env=None):
   """Executes a command and writes its stdout to a specified log file.
 
diff --git a/tools/perf/core/cli_helpers_unittest.py b/tools/perf/core/cli_helpers_unittest.py
index 36a07dc6..61a041fb 100644
--- a/tools/perf/core/cli_helpers_unittest.py
+++ b/tools/perf/core/cli_helpers_unittest.py
@@ -11,6 +11,8 @@
 from telemetry import decorators
 
 
+# https://crbug.com/938487
+@decorators.Disabled('all')
 class CLIHelpersTest(unittest.TestCase):
   def testUnsupportedColor(self):
     with self.assertRaises(AssertionError):
@@ -50,8 +52,6 @@
 
   @mock.patch('__builtin__.print')
   @mock.patch('__builtin__.raw_input')
-  # https://crbug.com/938575.
-  @decorators.Disabled('chromeos')
   def testAskAgainOnInvalidAnswer(self, raw_input_mock, print_mock):
     raw_input_mock.side_effect = ['foobar', 'y']
     self.assertTrue(cli_helpers.Ask('Ready?'))
@@ -63,8 +63,6 @@
 
   @mock.patch('__builtin__.print')
   @mock.patch('__builtin__.raw_input')
-  # https://crbug.com/938575.
-  @decorators.Disabled('chromeos')
   def testAskWithCustomAnswersAndDefault(self, raw_input_mock, print_mock):
     raw_input_mock.side_effect = ['']
     self.assertFalse(
@@ -74,8 +72,6 @@
 
   @mock.patch('__builtin__.print')
   @mock.patch('__builtin__.raw_input')
-  # https://crbug.com/938575.
-  @decorators.Disabled('chromeos')
   def testAskNoDefaultCustomAnswersAsList(self, raw_input_mock, print_mock):
     raw_input_mock.side_effect = ['', 'FoO']
     self.assertEqual(cli_helpers.Ask('Ready?', ['foo', 'bar']), 'foo')
@@ -85,8 +81,6 @@
       mock.call('\033[96mReady? [foo/bar] \033[0m', end=' ')
     ])
 
-  # https://crbug.com/937654.
-  @decorators.Disabled('android', 'chromeos')
   def testAskWithInvalidDefaultAnswer(self):
     with self.assertRaises(ValueError):
       cli_helpers.Ask('Ready?', ['foo', 'bar'], 'baz')
@@ -158,6 +152,16 @@
     with self.assertRaises(ValueError):
       cli_helpers.Run('cmd with args')
 
+  @mock.patch('__builtin__.print')
+  @mock.patch('__builtin__.raw_input')
+  def testPrompt(self, raw_input_mock, print_mock):
+    raw_input_mock.side_effect = ['', '42']
+    self.assertEqual(cli_helpers.Prompt(
+        'What is the ultimate meaning of life, universe and everything?'), '42')
+    self.assertEqual(raw_input_mock.call_count, 2)
+    self.assertEqual(print_mock.call_count, 3)
+
+
 
 if __name__ == "__main__":
   unittest.main()
diff --git a/ui/file_manager/integration_tests/file_manager/background.js b/ui/file_manager/integration_tests/file_manager/background.js
index c635458b..1fc3e3e 100644
--- a/ui/file_manager/integration_tests/file_manager/background.js
+++ b/ui/file_manager/integration_tests/file_manager/background.js
@@ -452,3 +452,20 @@
   await remoteCall.waitForElement(
       appId, `.tree-item[label="${directoryName}"]`);
 }
+
+/**
+ * Expands a tree item by clicking on its expand icon.
+ *
+ * @param {string} appId Files app windowId.
+ * @param {string} treeItem Query to the tree item that should be expanded.
+ * @return {Promise} Promise fulfilled on success.
+ */
+async function expandTreeItem(appId, treeItem) {
+  const expandIcon = treeItem + '> .tree-row[has-children=true] > .expand-icon';
+  await remoteCall.waitForElement(appId, expandIcon);
+  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
+      'fakeMouseClick', appId, [expandIcon]));
+
+  const expandedSubtree = treeItem + '> .tree-children[expanded]';
+  await remoteCall.waitForElement(appId, expandedSubtree);
+}
diff --git a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
index ef5fabb..74d58b1 100644
--- a/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/directory_tree_context_menu.js
@@ -204,6 +204,7 @@
       'focus failed: #directory-tree');
 
   // Right click desired item in the directory tree.
+  await remoteCall.waitForElement(appId, treeItemQuery);
   chrome.test.assertTrue(
       !!await remoteCall.callRemoteTestUtil(
           'fakeMouseRightClick', appId, [treeItemQuery]),
@@ -598,4 +599,169 @@
   await checkContextMenu(appId, query, menus, true /* rootMenu */);
 };
 
+/**
+ * Tests context menu for MyFiles, Downloads and sub-folder.
+ */
+testcase.dirContextMenuMyFiles = async () => {
+  const myFilesMenus = [
+    ['#share-with-linux', true],
+  ];
+  const downloadsMenus = [
+    ['#share-with-linux', true],
+    ['#new-folder', true],
+  ];
+  const photosMenus = [
+    ['#cut', true],
+    ['#copy', true],
+    ['#paste-into-folder', false],
+    ['#share-with-linux', true],
+    ['#rename', true],
+    ['#delete', true],
+    ['#new-folder', true],
+  ];
+  const myFilesQuery = '#directory-tree [entry-label="My files"]';
+  const downloadsQuery = '#directory-tree [entry-label="Downloads"]';
+  const photosQuery =
+      '#directory-tree [full-path-for-testing="/Downloads/photos"]';
+
+  // Open Files app on local Downloads.
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [ENTRIES.beautiful, ENTRIES.photos], []);
+
+  // Check the context menu is on desired state for MyFiles.
+  await checkContextMenu(
+      appId, myFilesQuery, myFilesMenus, true /* rootMenu */);
+
+  // Check the context menu for MyFiles>Downloads.
+  await checkContextMenu(
+      appId, downloadsQuery, downloadsMenus, false /* rootMenu */);
+
+  // Expand Downloads to display photos folder.
+  await expandTreeItem(appId, downloadsQuery);
+
+  // Check the context menu for MyFiles>Downloads>photos.
+  await checkContextMenu(appId, photosQuery, photosMenus, false /* rootMenu */);
+};
+
+/**
+ * Tests context menu for Crostini real root.
+ * TODO(lucmult): Check menus for a crostini folder.
+ */
+testcase.dirContextMenuCrostini = async () => {
+  const linuxMenus = [
+    ['#new-folder', true],
+  ];
+  const linuxQuery = '#directory-tree [entry-label="Linux files"]';
+
+  // Open Files app on local Downloads.
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Select Crostini, because the first right click doesn't show any context
+  // menu, just actually mounts crostini converting the tree item from fake to
+  // real root.
+  chrome.test.assertTrue(
+      !!await remoteCall.callRemoteTestUtil(
+          'fakeMouseClick', appId, [linuxQuery]),
+      'fakeMouseClick failed');
+
+  // Wait for the real root to appear.
+  await remoteCall.waitForElement(
+      appId,
+      '#directory-tree ' +
+          '[dir-type="SubDirectoryItem"][entry-label="Linux files"]');
+
+  // Check the context menu for Linux files.
+  await checkContextMenu(appId, linuxQuery, linuxMenus, false /* rootMenu */);
+};
+
+/**
+ * Tests context menu for ARC++/Play files root.
+ * TODO(lucmult): Check menus for a Play folder.
+ */
+testcase.dirContextMenuPlayFiles = async () => {
+  const playFilesMenus = [
+    ['#share-with-linux', true],
+    ['#new-folder', false],
+  ];
+  const playFilesQuery = '#directory-tree [entry-label="Play files"]';
+
+  // Open Files app on local Downloads.
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Check the context menu for Play files.
+  await checkContextMenu(
+      appId, playFilesQuery, playFilesMenus, false /* rootMenu */);
+};
+
+/**
+ * Tests context menu for USB root (single and multiple partitions).
+ * TODO(lucmult): Check menus for a USB folder.
+ */
+testcase.dirContextMenuUsbs = async () => {
+  const singleUsbMenus = [
+    ['#unmount', true],
+    ['#format', true],
+    ['#rename', false],
+    ['#share-with-linux', true],
+  ];
+  const partitionsRootMenus = [
+    ['#unmount', true],
+    ['#format', false],
+    ['#share-with-linux', true],
+  ];
+  const partition1Menus = [
+    ['#share-with-linux', true],
+    ['#rename', false],
+    ['#new-folder', true],
+  ];
+
+  const singleUsbQuery = '#directory-tree [entry-label="fake-usb"]';
+  const partitionsRootQuery = '#directory-tree [entry-label="Drive Label"]';
+  const partition1Query = '#directory-tree [entry-label="partition-1"]';
+
+  // Mount removable volumes.
+  await sendTestMessage({name: 'mountUsbWithPartitions'});
+  await sendTestMessage({name: 'mountFakeUsb'});
+
+  // Open Files app on local Downloads.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Check the context menu for single partition USB.
+  await checkContextMenu(
+      appId, singleUsbQuery, singleUsbMenus, true /* rootMenu */);
+
+  // Check the context menu for multiple partitions USB (root).
+  await checkContextMenu(
+      appId, partitionsRootQuery, partitionsRootMenus, true /* rootMenu */);
+
+  // Check the context menu for multiple partitions USB (actual partition).
+  await checkContextMenu(
+      appId, partition1Query, partition1Menus, false /* rootMenu */);
+};
+
+/**
+ * Tests context menu for FSP root.
+ * TODO(lucmult): Check menus for a FSP.
+ */
+testcase.dirContextMenuFsp = async () => {
+  const fspMenus = [
+    ['#unmount', true],
+  ];
+  const fspQuery = '#directory-tree [entry-label="Test (1)"]';
+
+  // Install a FSP.
+  const manifest = 'manifest_source_file.json';
+  await sendTestMessage({name: 'launchProviderExtension', manifest: manifest});
+
+  // Open Files app on local Downloads.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.beautiful], []);
+
+  // Check the context menu for FSP.
+  await checkContextMenu(appId, fspQuery, fspMenus, true /* rootMenu */);
+};
+
 })();
diff --git a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
index 9e19d9d..e50a59f 100644
--- a/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
+++ b/ui/file_manager/integration_tests/file_manager/folder_shortcuts.js
@@ -72,35 +72,16 @@
 };
 
 /**
- * Expands a directory tree item by clicking on its expand icon.
- *
- * @param {string} appId Files app windowId.
- * @param {Object} directory Directory whose tree item should be expanded.
- * @return {Promise} Promise fulfilled on success.
- */
-async function expandTreeItem(appId, directory) {
-  const expandIcon =
-      directory.treeItem + '> .tree-row[has-children=true] > .expand-icon';
-  await remoteCall.waitForElement(appId, expandIcon);
-  chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
-      'fakeMouseClick', appId, [expandIcon]));
-
-
-  const expandedSubtree = directory.treeItem + '> .tree-children[expanded]';
-  await remoteCall.waitForElement(appId, expandedSubtree);
-}
-
-/**
  * Expands whole directory tree under DIRECTORY.Drive.
  *
  * @param {string} appId Files app windowId.
  * @return {Promise} Promise fulfilled on success.
  */
 async function expandDirectoryTree(appId) {
-  await expandTreeItem(appId, DIRECTORY.Drive);
-  await expandTreeItem(appId, DIRECTORY.A);
-  await expandTreeItem(appId, DIRECTORY.B);
-  await expandTreeItem(appId, DIRECTORY.D);
+  await expandTreeItem(appId, DIRECTORY.Drive.treeItem);
+  await expandTreeItem(appId, DIRECTORY.A.treeItem);
+  await expandTreeItem(appId, DIRECTORY.B.treeItem);
+  await expandTreeItem(appId, DIRECTORY.D.treeItem);
 }
 
 /**
diff --git a/ui/gfx/image/image.cc b/ui/gfx/image/image.cc
index 8e2b0d9..d7237707 100644
--- a/ui/gfx/image/image.cc
+++ b/ui/gfx/image/image.cc
@@ -148,11 +148,8 @@
 
 class ImageRepSkia : public ImageRep {
  public:
-  // Takes ownership of |image|.
-  explicit ImageRepSkia(ImageSkia* image)
-      : ImageRep(Image::kImageRepSkia),
-        image_(image) {
-  }
+  explicit ImageRepSkia(std::unique_ptr<ImageSkia> image)
+      : ImageRep(Image::kImageRepSkia), image_(std::move(image)) {}
 
   ~ImageRepSkia() override {}
 
@@ -345,8 +342,8 @@
 Image::Image(const ImageSkia& image) {
   if (!image.isNull()) {
     storage_ = new internal::ImageStorage(Image::kImageRepSkia);
-    AddRepresentation(
-        std::make_unique<internal::ImageRepSkia>(new ImageSkia(image)));
+    AddRepresentation(std::make_unique<internal::ImageRepSkia>(
+        std::make_unique<ImageSkia>(image)));
   }
 }
 
@@ -425,16 +422,16 @@
         const internal::ImageRepCocoaTouch* native_rep =
             GetRepresentation(kImageRepCocoaTouch, true)
                 ->AsImageRepCocoaTouch();
-        scoped_rep.reset(new internal::ImageRepSkia(
-            new ImageSkia(ImageSkiaFromUIImage(native_rep->image()))));
+        scoped_rep.reset(new internal::ImageRepSkia(std::make_unique<ImageSkia>(
+            ImageSkiaFromUIImage(native_rep->image()))));
         break;
       }
 #elif defined(OS_MACOSX)
       case kImageRepCocoa: {
         const internal::ImageRepCocoa* native_rep =
             GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
-        scoped_rep.reset(new internal::ImageRepSkia(
-            new ImageSkia(ImageSkiaFromNSImage(native_rep->image()))));
+        scoped_rep.reset(new internal::ImageRepSkia(std::make_unique<ImageSkia>(
+            ImageSkiaFromNSImage(native_rep->image()))));
         break;
       }
 #endif
diff --git a/ui/gfx/image/image_generic.cc b/ui/gfx/image/image_generic.cc
index ad77df1..9d90008 100644
--- a/ui/gfx/image/image_generic.cc
+++ b/ui/gfx/image/image_generic.cc
@@ -4,6 +4,7 @@
 
 #include "ui/gfx/image/image_platform.h"
 
+#include <memory>
 #include <set>
 #include <utility>
 
@@ -16,12 +17,11 @@
 namespace {
 
 // Returns a 16x16 red image to visually show error in decoding PNG.
-// Caller takes ownership of returned ImageSkia.
-ImageSkia* GetErrorImageSkia() {
+std::unique_ptr<ImageSkia> GetErrorImageSkia() {
   SkBitmap bitmap;
   bitmap.allocN32Pixels(16, 16);
   bitmap.eraseARGB(0xff, 0xff, 0, 0);
-  return new ImageSkia(ImageSkiaRep(bitmap, 1.0f));
+  return std::make_unique<ImageSkia>(ImageSkiaRep(bitmap, 1.0f));
 }
 
 class PNGImageSource : public ImageSkiaSource {
@@ -90,7 +90,8 @@
 
 }  // namespace
 
-ImageSkia* ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps) {
+std::unique_ptr<ImageSkia> ImageSkiaFromPNG(
+    const std::vector<ImagePNGRep>& image_png_reps) {
   if (image_png_reps.empty())
     return GetErrorImageSkia();
   std::unique_ptr<PNGImageSource> image_source(new PNGImageSource);
@@ -103,7 +104,7 @@
   DCHECK(!size.IsEmpty());
   if (size.IsEmpty())
     return GetErrorImageSkia();
-  return new ImageSkia(std::move(image_source), size);
+  return std::make_unique<ImageSkia>(std::move(image_source), size);
 }
 
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
diff --git a/ui/gfx/image/image_ios.mm b/ui/gfx/image/image_ios.mm
index 4b1c67c..1272728 100644
--- a/ui/gfx/image/image_ios.mm
+++ b/ui/gfx/image/image_ios.mm
@@ -109,11 +109,11 @@
   return Get1xPNGBytesFromUIImage(image);
 }
 
-ImageSkia* ImageSkiaFromPNG(
+std::unique_ptr<ImageSkia> ImageSkiaFromPNG(
     const std::vector<gfx::ImagePNGRep>& image_png_reps) {
   // iOS does not expose libpng, so conversion from PNG to ImageSkia must go
   // through UIImage.
-  gfx::ImageSkia* image_skia = new gfx::ImageSkia();
+  auto image_skia = std::make_unique<gfx::ImageSkia>();
   for (size_t i = 0; i < image_png_reps.size(); ++i) {
     base::scoped_nsobject<UIImage> uiimage(
         CreateUIImageFromImagePNGRep(image_png_reps[i]));
diff --git a/ui/gfx/image/image_platform.h b/ui/gfx/image/image_platform.h
index 117d2624e..6eeebe3 100644
--- a/ui/gfx/image/image_platform.h
+++ b/ui/gfx/image/image_platform.h
@@ -11,6 +11,7 @@
 #ifndef UI_GFX_IMAGE_IMAGE_PLATFORM_H_
 #define UI_GFX_IMAGE_IMAGE_PLATFORM_H_
 
+#include <memory>
 #include <vector>
 
 #include "base/memory/ref_counted.h"
@@ -45,7 +46,8 @@
 gfx::Size NSImageSize(NSImage* image);
 #endif  // defined(OS_MACOSX)
 
-ImageSkia* ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps);
+std::unique_ptr<ImageSkia> ImageSkiaFromPNG(
+    const std::vector<ImagePNGRep>& image_png_reps);
 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
     const ImageSkia* image_skia);
 
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
index acf10353..175f0b6 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -103,6 +103,12 @@
   frame->presentation_callback = std::move(presentation_callback);
   unsubmitted_frames_.push_back(std::make_unique<PendingFrame>());
 
+  if (!use_egl_fence_sync_) {
+    frame->ready = true;
+    SubmitFrame();
+    return;
+  }
+
   // TODO: the following should be replaced by a per surface flush as it gets
   // implemented in GL drivers.
   EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
@@ -156,6 +162,10 @@
   return config_;
 }
 
+void GbmSurfacelessWayland::SetRelyOnImplicitSync() {
+  use_egl_fence_sync_ = false;
+}
+
 GbmSurfacelessWayland::~GbmSurfacelessWayland() {
   surface_factory_->UnregisterSurface(widget_);
 }
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
index 89be735..6870debb 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
+++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -53,6 +53,7 @@
                           SwapCompletionCallback completion_callback,
                           PresentationCallback presentation_callback) override;
   EGLConfig GetConfig() override;
+  void SetRelyOnImplicitSync() override;
 
  private:
   ~GbmSurfacelessWayland() override;
@@ -95,6 +96,7 @@
   std::unique_ptr<PendingFrame> submitted_frame_;
   bool has_implicit_external_sync_;
   bool last_swap_buffers_result_ = true;
+  bool use_egl_fence_sync_ = true;
 
   base::WeakPtrFactory<GbmSurfacelessWayland> weak_factory_;
 
diff --git a/ui/views/controls/label_perftest.cc b/ui/views/controls/label_perftest.cc
index bd0094d..e8b5c59 100644
--- a/ui/views/controls/label_perftest.cc
+++ b/ui/views/controls/label_perftest.cc
@@ -5,7 +5,7 @@
 #include "ui/views/controls/label.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "cc/base/lap_timer.h"
+#include "base/timer/lap_timer.h"
 #include "testing/perf/perf_test.h"
 #include "ui/views/test/views_test_base.h"
 
@@ -28,7 +28,7 @@
 
   // The time limit is unused. Use kLaps for the check interval so the time is
   // only measured once.
-  cc::LapTimer timer(kWarmupLaps, base::TimeDelta(), kLaps);
+  base::LapTimer timer(kWarmupLaps, base::TimeDelta(), kLaps);
   for (int i = 0; i < kLaps + kWarmupLaps; ++i) {
     label.SetText(i % 2 == 0 ? string1 : string2);
     label.GetPreferredSize();