diff --git a/.gitignore b/.gitignore
index 8a02255..2600373 100644
--- a/.gitignore
+++ b/.gitignore
@@ -194,6 +194,7 @@
 /ios/third_party/earl_grey/src
 /ios/third_party/fishhook/src
 /ios/third_party/gcdwebserver/src
+/ios/third_party/gtx/src
 /ios/third_party/material_components_ios/src
 /ios/third_party/material_font_disk_loader_ios/src
 /ios/third_party/material_internationalization_ios/src
diff --git a/DEPS b/DEPS
index deacce1f..dea94fd 100644
--- a/DEPS
+++ b/DEPS
@@ -275,6 +275,11 @@
       'condition': 'checkout_ios',
   },
 
+  'src/ios/third_party/gtx/src': {
+      'url': Var('chromium_git') + '/external/github.com/google/GTXiLib.git' + '@' + '2d2bf1408cfee8559cd672129959bba54a5cfa2b',
+      'condition': 'checkout_ios',
+  },
+
   'src/ios/third_party/firebase': {
       'packages': [
         {
@@ -563,7 +568,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4b90891429276fd084d34711a611e1131b9dab69',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '4cc579e9d288a8d5a2005ba4efbf29c829cb9563',
       'condition': 'checkout_linux',
   },
 
@@ -588,7 +593,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '85f5e7ccd1af327cf210e696f782a1533d625b3c',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '29361ca11fa37bfe9961af093edaf60c37d2db96',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -938,7 +943,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '465d65ba9903a3d276fc319b9ecaca9174419bea',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'a4238afbd1d086db841126957d779b506dc4e2b6',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1071,7 +1076,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7ca87fb1d3da3b3d2060886e8c58e726d74c8219',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4d95f1eb9beb7cf360e119fae69aa39f7463f02d',
+    Var('webrtc_git') + '/src.git' + '@' + 'efbcb31cb67e3090b82c09ed5aabc4bbc53f37be',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1105,7 +1110,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ec5364ee5d5a600e5ddacda19636abf13f618962',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2916d264903ab944981cc037c05b27f0d41905c3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/net/aw_url_request_context_getter_unittest.cc b/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
index e031d0f3..3aa7356 100644
--- a/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
@@ -121,7 +121,7 @@
   std::unique_ptr<net::CertVerifier::Request> request;
   int error = context->cert_verifier()->Verify(
       net::CertVerifier::RequestParams(cert, "www.ahrn.com", flags,
-                                       std::string(), net::CertificateList()),
+                                       std::string()),
       &result, callback.callback(), &request, net::NetLogWithSource());
   EXPECT_THAT(error, net::test::IsError(net::ERR_IO_PENDING));
   EXPECT_TRUE(request);
@@ -158,8 +158,7 @@
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
   int error = context->cert_verifier()->Verify(
-      net::CertVerifier::RequestParams(cert, "127.0.0.1", flags, std::string(),
-                                       net::CertificateList()),
+      net::CertVerifier::RequestParams(cert, "127.0.0.1", flags, std::string()),
       &result, callback.callback(), &request, net::NetLogWithSource());
   EXPECT_THAT(error, net::test::IsError(net::ERR_IO_PENDING));
   EXPECT_TRUE(request);
diff --git a/ash/assistant/ui/assistant_mini_view.cc b/ash/assistant/ui/assistant_mini_view.cc
index b919134..641d9b4ec 100644
--- a/ash/assistant/ui/assistant_mini_view.cc
+++ b/ash/assistant/ui/assistant_mini_view.cc
@@ -158,10 +158,12 @@
       break;
     case InputModality::kKeyboard:
     case InputModality::kVoice:
-      // If we've cached an active query, we'll use that as our prompt. If not,
-      // we fall back to our default prompt string.
+      // If we've cached an active query, we'll use that as our prompt provided
+      // it is non-empty. If not, we fall back to our default prompt string.
+      // TODO(dmblack): Once b/112000321 is fixed we should remove empty query
+      // handling as that should only occur due to an invalid interaction state.
       label_->SetText(
-          last_active_query_.has_value()
+          last_active_query_.has_value() && !last_active_query_.value().empty()
               ? base::UTF8ToUTF16(last_active_query_.value())
               : l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_PROMPT_DEFAULT));
       break;
diff --git a/ash/display/window_tree_host_manager_unittest.cc b/ash/display/window_tree_host_manager_unittest.cc
index 988f5f93..93e9b3e 100644
--- a/ash/display/window_tree_host_manager_unittest.cc
+++ b/ash/display/window_tree_host_manager_unittest.cc
@@ -1682,7 +1682,7 @@
   views::MouseWatcher watcher(
       std::make_unique<views::MouseWatcherViewHost>(view, gfx::Insets()),
       &listener);
-  watcher.Start();
+  watcher.Start(root2);
 
   ui::test::EventGenerator event_generator(
       widget->GetNativeWindow()->GetRootWindow());
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 5cae8a7..775b6793 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -177,7 +177,7 @@
     // We need to transfer the current gesture sequence and the GR's touch event
     // queue to the |drag_drop_tracker_|'s capture window so that when it takes
     // capture, it still gets a valid gesture state.
-    ui::GestureRecognizer::Get()->TransferEventsTo(
+    Shell::Get()->aura_env()->gesture_recognizer()->TransferEventsTo(
         source_window, tracker->capture_window(),
         ui::GestureRecognizer::ShouldCancelTouches::Cancel);
     // We also send a gesture end to the source window so it can clear state.
diff --git a/ash/login/ui/lock_window.cc b/ash/login/ui/lock_window.cc
index dfe1567..73928b17 100644
--- a/ash/login/ui/lock_window.cc
+++ b/ash/login/ui/lock_window.cc
@@ -12,8 +12,6 @@
 namespace ash {
 
 LockWindow::LockWindow() {
-  ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
-
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.delegate = this;
@@ -25,6 +23,7 @@
                                         kShellWindowId_LockScreenContainer);
   }
   Init(params);
+  GetGestureRecognizer()->CancelActiveTouchesExcept(nullptr);
   SetVisibilityAnimationTransition(views::Widget::ANIMATE_NONE);
 
   // Disable virtual keyboard overscroll because it interferes with scrolling
diff --git a/ash/system/bluetooth/bluetooth_detailed_view.cc b/ash/system/bluetooth/bluetooth_detailed_view.cc
index 175b4002..86ad558 100644
--- a/ash/system/bluetooth/bluetooth_detailed_view.cc
+++ b/ash/system/bluetooth/bluetooth_detailed_view.cc
@@ -358,6 +358,8 @@
 
   toggle_ =
       TrayPopupUtils::CreateToggleButton(this, IDS_ASH_STATUS_TRAY_BLUETOOTH);
+  toggle_->SetIsOn(Shell::Get()->tray_bluetooth_helper()->GetBluetoothEnabled(),
+                   false /* animate */);
   tri_view()->AddView(TriView::Container::END, toggle_);
 
   settings_ = CreateSettingsButton(IDS_ASH_STATUS_TRAY_BLUETOOTH_SETTINGS);
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index 3d3befb..f78feac9 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -510,7 +510,7 @@
   const SkColor icon_color = GetDefaultColorForIconType(icon_type_);
   if (type == shill::kTypeWifi) {
     if (network->security_class() != shill::kSecurityNone &&
-        IconTypeIsDark(icon_type_)) {
+        !IsTrayIcon(icon_type_)) {
       badges->bottom_right = {&kNetworkBadgeSecureIcon, icon_color};
     }
   } else if (type == shill::kTypeWimax) {
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 3043292..6cae145f 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -144,9 +144,9 @@
 constexpr SkColor kUnifiedMenuSecondaryTextColor =
     SkColorSetA(kUnifiedMenuIconColor, 0xa3);
 constexpr SkColor kUnifiedMenuIconColorDisabled =
-    SkColorSetA(kUnifiedMenuIconColor, 0xa3);
+    SkColorSetRGB(0x5f, 0x63, 0x68);
 constexpr SkColor kUnifiedMenuTextColorDisabled =
-    SkColorSetA(kUnifiedMenuTextColor, 0xa3);
+    SkColorSetRGB(0x5f, 0x63, 0x68);
 constexpr SkColor kUnifiedMenuButtonColor =
     SkColorSetA(kUnifiedMenuIconColor, 0x14);
 constexpr SkColor kUnifiedMenuSeparatorColor =
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 011b654..3501f553 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -51,7 +51,7 @@
                                       ? ::wm::WINDOW_MOVE_SOURCE_TOUCH
                                       : ::wm::WINDOW_MOVE_SOURCE_MOUSE;
   if (gesture_target) {
-    ui::GestureRecognizer::Get()->TransferEventsTo(
+    window->env()->gesture_recognizer()->TransferEventsTo(
         gesture_target, window,
         ui::GestureRecognizer::ShouldCancelTouches::DontCancel);
   }
@@ -71,8 +71,9 @@
   if (move_source == ::wm::WINDOW_MOVE_SOURCE_TOUCH &&
       Shell::Get()->aura_env()->is_touch_down()) {
     gfx::PointF drag_location_f;
-    bool has_point = ui::GestureRecognizer::Get()->GetLastTouchPointForTarget(
-        source, &drag_location_f);
+    bool has_point =
+        source->env()->gesture_recognizer()->GetLastTouchPointForTarget(
+            source, &drag_location_f);
     drag_location = gfx::ToFlooredPoint(drag_location_f);
     DCHECK(has_point);
   } else {
diff --git a/ash/wm/workspace/multi_window_resize_controller.cc b/ash/wm/workspace/multi_window_resize_controller.cc
index 487ccc9..b4db01e 100644
--- a/ash/wm/workspace/multi_window_resize_controller.cc
+++ b/ash/wm/workspace/multi_window_resize_controller.cc
@@ -295,7 +295,8 @@
       std::make_unique<ResizeMouseWatcherHost>(this), this);
   mouse_watcher_->set_notify_on_exit_time(
       base::TimeDelta::FromMilliseconds(kHideDelayMS));
-  mouse_watcher_->Start();
+  DCHECK(resize_widget_);
+  mouse_watcher_->Start(resize_widget_->GetNativeWindow());
 }
 
 MultiWindowResizeController::ResizeWindows
diff --git a/ash/wm/workspace/multi_window_resize_controller_unittest.cc b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
index 6142d45..180d7a7 100644
--- a/ash/wm/workspace/multi_window_resize_controller_unittest.cc
+++ b/ash/wm/workspace/multi_window_resize_controller_unittest.cc
@@ -23,7 +23,6 @@
 #include "ui/aura/window.h"
 #include "ui/base/class_property.h"
 #include "ui/base/hit_test.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
@@ -322,12 +321,6 @@
 
 // Tests that clicking outside of the resize handle dismisses it.
 TEST_F(MultiWindowResizeControllerTest, ClickOutside) {
-  // TODO(sky): get this test working with single-process-mash. This ends up
-  // using EventMonitorAura, which accesses Env::GetInstance().
-  // https://crbug.com/874481
-  if (::features::IsSingleProcessMash())
-    return;
-
   aura::test::TestWindowDelegate delegate1;
   std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
       &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
diff --git a/base/memory/read_only_shared_memory_region.h b/base/memory/read_only_shared_memory_region.h
index 4f92762..c7d1483 100644
--- a/base/memory/read_only_shared_memory_region.h
+++ b/base/memory/read_only_shared_memory_region.h
@@ -32,6 +32,12 @@
   // This means that the caller's process is the only process that can modify
   // the region content. If you need to pass write access to another process,
   // consider using WritableSharedMemoryRegion or UnsafeSharedMemoryRegion.
+  //
+  // This call will fail if the process does not have sufficient permissions to
+  // create a shared memory region itself. See
+  // mojo::CreateReadOnlySharedMemoryRegion in
+  // mojo/public/cpp/base/shared_memory_utils.h for creating a shared memory
+  // region from a an unprivileged process where a broker must be used.
   static MappedReadOnlyRegion Create(size_t size);
 
   // Returns a ReadOnlySharedMemoryRegion built from a platform-specific handle
diff --git a/base/memory/unsafe_shared_memory_region.h b/base/memory/unsafe_shared_memory_region.h
index ea637cd5..98a84325 100644
--- a/base/memory/unsafe_shared_memory_region.h
+++ b/base/memory/unsafe_shared_memory_region.h
@@ -31,6 +31,12 @@
   using MappingType = WritableSharedMemoryMapping;
   // Creates a new UnsafeSharedMemoryRegion instance of a given size that can be
   // used for mapping writable shared memory into the virtual address space.
+  //
+  // This call will fail if the process does not have sufficient permissions to
+  // create a shared memory region itself. See
+  // mojo::CreateUnsafeSharedMemoryRegion in
+  // mojo/public/cpp/base/shared_memory_utils.h for creating a shared memory
+  // region from a an unprivileged process where a broker must be used.
   static UnsafeSharedMemoryRegion Create(size_t size);
 
   // Returns an UnsafeSharedMemoryRegion built from a platform-specific handle
diff --git a/base/memory/writable_shared_memory_region.h b/base/memory/writable_shared_memory_region.h
index f656db1..edd25aa51 100644
--- a/base/memory/writable_shared_memory_region.h
+++ b/base/memory/writable_shared_memory_region.h
@@ -28,6 +28,12 @@
   // Creates a new WritableSharedMemoryRegion instance of a given
   // size that can be used for mapping writable shared memory into the virtual
   // address space.
+  //
+  // This call will fail if the process does not have sufficient permissions to
+  // create a shared memory region itself. See
+  // mojo::CreateWritableSharedMemoryRegion in
+  // mojo/public/cpp/base/shared_memory_utils.h for creating a shared memory
+  // region from a an unprivileged process where a broker must be used.
   static WritableSharedMemoryRegion Create(size_t size);
 
   // Returns a WritableSharedMemoryRegion built from a platform handle that was
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index 04c17372..4323af2 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -57,7 +57,9 @@
 
 void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) {
   void* address = self->next->alloc_function(self->next, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, size, kSkipBaseAllocatorFrames);
+  SamplingHeapProfiler::RecordAlloc(address, size,
+                                    SamplingHeapProfiler::kMalloc, nullptr,
+                                    kSkipBaseAllocatorFrames);
   return address;
 }
 
@@ -68,6 +70,7 @@
   void* address =
       self->next->alloc_zero_initialized_function(self->next, n, size, context);
   SamplingHeapProfiler::RecordAlloc(address, n * size,
+                                    SamplingHeapProfiler::kMalloc, nullptr,
                                     kSkipBaseAllocatorFrames);
   return address;
 }
@@ -78,7 +81,9 @@
                      void* context) {
   void* address =
       self->next->alloc_aligned_function(self->next, alignment, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, size, kSkipBaseAllocatorFrames);
+  SamplingHeapProfiler::RecordAlloc(address, size,
+                                    SamplingHeapProfiler::kMalloc, nullptr,
+                                    kSkipBaseAllocatorFrames);
   return address;
 }
 
@@ -89,7 +94,9 @@
   // Note: size == 0 actually performs free.
   SamplingHeapProfiler::RecordFree(address);
   address = self->next->realloc_function(self->next, address, size, context);
-  SamplingHeapProfiler::RecordAlloc(address, size, kSkipBaseAllocatorFrames);
+  SamplingHeapProfiler::RecordAlloc(address, size,
+                                    SamplingHeapProfiler::kMalloc, nullptr,
+                                    kSkipBaseAllocatorFrames);
   return address;
 }
 
@@ -113,6 +120,7 @@
       self->next, size, results, num_requested, context);
   for (unsigned i = 0; i < num_allocated; ++i) {
     SamplingHeapProfiler::RecordAlloc(results[i], size,
+                                      SamplingHeapProfiler::kMalloc, nullptr,
                                       kSkipBaseAllocatorFrames);
   }
   return num_allocated;
@@ -150,7 +158,8 @@
 #if BUILDFLAG(USE_PARTITION_ALLOC) && !defined(OS_NACL)
 
 void PartitionAllocHook(void* address, size_t size, const char*) {
-  SamplingHeapProfiler::RecordAlloc(address, size);
+  SamplingHeapProfiler::RecordAlloc(
+      address, size, SamplingHeapProfiler::kPartitionAlloc, nullptr);
 }
 
 void PartitionFreeHook(void* address) {
@@ -288,6 +297,8 @@
 // static
 void SamplingHeapProfiler::RecordAlloc(void* address,
                                        size_t size,
+                                       AllocatorType type,
+                                       const char* context,
                                        uint32_t skip_frames) {
   if (UNLIKELY(!base::subtle::NoBarrier_Load(&g_running)))
     return;
@@ -316,7 +327,8 @@
 
   AccumulatedBytesTLS().Set(reinterpret_cast<void*>(accumulated_bytes));
 
-  instance_->DoRecordAlloc(samples * mean_interval, size, address, skip_frames);
+  instance_->DoRecordAlloc(samples * mean_interval, size, address, type,
+                           context, skip_frames);
 }
 
 void SamplingHeapProfiler::RecordStackTrace(Sample* sample,
@@ -353,6 +365,8 @@
 void SamplingHeapProfiler::DoRecordAlloc(size_t total_allocated,
                                          size_t size,
                                          void* address,
+                                         AllocatorType type,
+                                         const char* context,
                                          uint32_t skip_frames) {
   if (entered_.Get())
     return;
@@ -362,7 +376,7 @@
     Sample sample(size, total_allocated, ++last_sample_ordinal_);
     RecordStackTrace(&sample, skip_frames);
     for (auto* observer : observers_)
-      observer->SampleAdded(sample.ordinal, size, total_allocated);
+      observer->SampleAdded(address, size, total_allocated, type, context);
     samples_.emplace(address, std::move(sample));
     // TODO(alph): Sometimes RecordAlloc is called twice in a row without
     // a RecordFree in between. Investigate it.
@@ -392,7 +406,7 @@
     auto it = samples_.find(address);
     CHECK(it != samples_.end());
     for (auto* observer : observers_)
-      observer->SampleRemoved(it->second.ordinal);
+      observer->SampleRemoved(address);
     samples_.erase(it);
     sampled_addresses_set().Remove(address);
   }
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.h b/base/sampling_heap_profiler/sampling_heap_profiler.h
index ea50e67..1ef7f3f 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.h
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.h
@@ -29,6 +29,8 @@
 // The recorded samples can then be retrieved using GetSamples method.
 class BASE_EXPORT SamplingHeapProfiler {
  public:
+  enum AllocatorType : uint32_t { kMalloc, kPartitionAlloc, kBlinkGC, kMax };
+
   class BASE_EXPORT Sample {
    public:
     Sample(const Sample&);
@@ -49,8 +51,12 @@
   class SamplesObserver {
    public:
     virtual ~SamplesObserver() = default;
-    virtual void SampleAdded(uint32_t id, size_t size, size_t total) = 0;
-    virtual void SampleRemoved(uint32_t id) = 0;
+    virtual void SampleAdded(void* address,
+                             size_t size,
+                             size_t total,
+                             AllocatorType type,
+                             const char* context) = 0;
+    virtual void SampleRemoved(void* address) = 0;
   };
 
   // Must be called early during the process initialization. It creates and
@@ -77,7 +83,11 @@
 
   std::vector<Sample> GetSamples(uint32_t profile_id);
 
-  static void RecordAlloc(void* address, size_t, uint32_t skip_frames = 0);
+  static void RecordAlloc(void* address,
+                          size_t,
+                          AllocatorType,
+                          const char* context,
+                          uint32_t skip_frames = 0);
   static void RecordFree(void* address);
 
   static SamplingHeapProfiler* GetInstance();
@@ -93,6 +103,8 @@
   void DoRecordAlloc(size_t total_allocated,
                      size_t allocation_size,
                      void* address,
+                     AllocatorType type,
+                     const char* context,
                      uint32_t skip_frames);
   void DoRecordFree(void* address);
   void RecordStackTrace(Sample*, uint32_t skip_frames);
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc b/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc
index 6602e6c..21aebf5 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc
@@ -26,15 +26,19 @@
  public:
   explicit SamplesCollector(size_t watch_size) : watch_size_(watch_size) {}
 
-  void SampleAdded(uint32_t id, size_t size, size_t) override {
+  void SampleAdded(void* address,
+                   size_t size,
+                   size_t,
+                   SamplingHeapProfiler::AllocatorType,
+                   const char*) override {
     if (sample_added || size != watch_size_)
       return;
-    sample_id_ = id;
+    sample_address_ = address;
     sample_added = true;
   }
 
-  void SampleRemoved(uint32_t id) override {
-    if (id == sample_id_)
+  void SampleRemoved(void* address) override {
+    if (address == sample_address_)
       sample_removed = true;
   }
 
@@ -43,7 +47,7 @@
 
  private:
   size_t watch_size_;
-  uint32_t sample_id_ = 0;
+  void* sample_address_ = nullptr;
 };
 
 TEST_F(SamplingHeapProfilerTest, CollectSamples) {
diff --git a/build/config/ios/rules.gni b/build/config/ios/rules.gni
index 02725177..063b964 100644
--- a/build/config/ios/rules.gni
+++ b/build/config/ios/rules.gni
@@ -7,6 +7,11 @@
 import("//build/config/mac/symbols.gni")
 import("//build/toolchain/toolchain.gni")
 
+# Constants corresponding to the bundle type identifier for XCTest and XCUITest
+# targets.
+_ios_xcode_xctest_bundle_id = "com.apple.product-type.bundle.unit-test"
+_ios_xcode_xcuitest_bundle_id = "com.apple.product-type.bundle.ui-testing"
+
 # Invokes lipo on multiple arch-specific binaries to create a fat binary.
 #
 # Arguments
@@ -210,8 +215,8 @@
 
   if (defined(invoker.xcode_test_application_name)) {
     assert(
-        invoker.product_type == "com.apple.product-type.bundle.unit-test" ||
-            invoker.product_type == "com.apple.product-type.bundle.ui-testing",
+        invoker.product_type == _ios_xcode_xctest_bundle_id ||
+            invoker.product_type == _ios_xcode_xcuitest_bundle_id,
         "xcode_test_application_name can be only defined for Xcode unit or ui test target.")
   }
 
@@ -285,7 +290,6 @@
                              "public_deps",
                              "testonly",
                              "visibility",
-                             "xcode_extra_attributes",
                              "xcode_test_application_name",
                            ])
 
@@ -299,6 +303,15 @@
       public_deps = []
     }
 
+    xcode_extra_attributes = {
+      IPHONEOS_DEPLOYMENT_TARGET = ios_deployment_target
+
+      # If invoker has defined extra attributes, they override the defaults.
+      if (defined(invoker.xcode_extra_attributes)) {
+        forward_variables_from(invoker.xcode_extra_attributes, "*")
+      }
+    }
+
     if (defined(invoker.bundle_binary_target)) {
       public_deps += [ invoker.bundle_binary_target ]
     }
@@ -1574,8 +1587,8 @@
   assert(defined(invoker.deps), "deps must be defined for $target_name")
   assert(defined(invoker.product_type),
          "product_type must be defined for $target_name")
-  assert(invoker.product_type == "com.apple.product-type.bundle.unit-test" ||
-             invoker.product_type == "com.apple.product-type.bundle.ui-testing",
+  assert(invoker.product_type == _ios_xcode_xctest_bundle_id ||
+             invoker.product_type == _ios_xcode_xcuitest_bundle_id,
          "product_type defined for $target_name is invalid.")
   assert(defined(invoker.host_target),
          "host_target must be defined for $target_name")
@@ -1712,9 +1725,18 @@
 
         # For XCUITest, Xcode requires specifying the host application name via
         # the TEST_TARGET_NAME attribute.
-        if (invoker.product_type == "com.apple.product-type.bundle.ui-testing") {
+        if (invoker.product_type == _ios_xcode_xcuitest_bundle_id) {
           TEST_TARGET_NAME = invoker.xcode_test_application_name
         }
+
+        # For XCTest, Xcode requires specifying the host application path via
+        # both BUNDLE_LOADER and TEST_HOST attributes.
+        if (invoker.product_type == _ios_xcode_xctest_bundle_id) {
+          BUNDLE_LOADER = "\$(TEST_HOST)"
+          TEST_HOST =
+              "\$(BUILT_PRODUCTS_DIR)/${invoker.xcode_test_application_name}" +
+              ".app/${invoker.xcode_test_application_name}"
+        }
       }
 
       deps = [
@@ -1773,7 +1795,7 @@
 
   ios_xctest_bundle(_xctest_target) {
     output_name = _xctest_output
-    product_type = "com.apple.product-type.bundle.unit-test"
+    product_type = _ios_xcode_xctest_bundle_id
     host_target = _host_target
     xcode_test_application_name = _host_output
 
@@ -2011,7 +2033,7 @@
   ios_xctest_bundle(_xcuitest_module_target) {
     forward_variables_from(invoker, [ "xcode_test_application_name" ])
 
-    product_type = "com.apple.product-type.bundle.ui-testing"
+    product_type = _ios_xcode_xcuitest_bundle_id
     host_target = _xcuitest_runner_target
     output_name = _xcuitest_module_output
 
diff --git a/build/toolchain/cros/BUILD.gn b/build/toolchain/cros/BUILD.gn
index bf139dc..5a9561f 100644
--- a/build/toolchain/cros/BUILD.gn
+++ b/build/toolchain/cros/BUILD.gn
@@ -12,6 +12,16 @@
   ar = cros_target_ar
   cc = cros_target_cc
   cxx = cros_target_cxx
+
+  # Relativize path if compiler is specified such that not to lookup from $PATH
+  # and cc/cxx does not contain additional flags.
+  if (cc != get_path_info(cc, "file") && string_replace(cc, " ", "") == cc) {
+    cc = rebase_path(cc, root_build_dir)
+  }
+  if (cxx != get_path_info(cxx, "file") && string_replace(cxx, " ", "") == cxx) {
+    cxx = rebase_path(cxx, root_build_dir)
+  }
+
   ld = cxx
   if (cros_target_ld != "") {
     ld = cros_target_ld
@@ -46,6 +56,15 @@
   ar = cros_target_ar
   cc = cros_target_cc
   cxx = cros_target_cxx
+
+  # Relativize path if compiler is specified such that not to lookup from $PATH
+  # and cc/cxx does not contain additional flags.
+  if (cc != get_path_info(cc, "file") && string_replace(cc, " ", "") == cc) {
+    cc = rebase_path(cc, root_build_dir)
+  }
+  if (cxx != get_path_info(cxx, "file") && string_replace(cxx, " ", "") == cxx) {
+    cxx = rebase_path(cxx, root_build_dir)
+  }
   ld = cxx
   if (cros_target_ld != "") {
     ld = cros_target_ld
@@ -77,6 +96,15 @@
   ar = cros_host_ar
   cc = cros_host_cc
   cxx = cros_host_cxx
+
+  # Relativize path if compiler is specified such that not to lookup from $PATH
+  # and cc/cxx does not contain additional flags.
+  if (cc != get_path_info(cc, "file") && string_replace(cc, " ", "") == cc) {
+    cc = rebase_path(cc, root_build_dir)
+  }
+  if (cxx != get_path_info(cxx, "file") && string_replace(cxx, " ", "") == cxx) {
+    cxx = rebase_path(cxx, root_build_dir)
+  }
   ld = cxx
   if (cros_host_ld != "") {
     ld = cros_host_ld
@@ -106,6 +134,15 @@
   ar = cros_v8_snapshot_ar
   cc = cros_v8_snapshot_cc
   cxx = cros_v8_snapshot_cxx
+
+  # Relativize path if compiler is specified such that not to lookup from $PATH
+  # and cc/cxx does not contain additional flags.
+  if (cc != get_path_info(cc, "file") && string_replace(cc, " ", "") == cc) {
+    cc = rebase_path(cc, root_build_dir)
+  }
+  if (cxx != get_path_info(cxx, "file") && string_replace(cxx, " ", "") == cxx) {
+    cxx = rebase_path(cxx, root_build_dir)
+  }
   ld = cxx
   if (cros_v8_snapshot_ld != "") {
     ld = cros_v8_snapshot_ld
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 0b3aca66..a2ca6a7 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -1072,7 +1072,7 @@
       SkScalar intervals[] = {1.f, 1.f};
       flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
       flags.setMaskFilter(SkMaskFilter::MakeBlur(
-          SkBlurStyle::kOuter_SkBlurStyle, 4.3f, test_rects[0]));
+          SkBlurStyle::kOuter_SkBlurStyle, 4.3f));
       flags.setColorFilter(SkColorMatrixFilter::MakeLightingFilter(
           SK_ColorYELLOW, SK_ColorGREEN));
 
diff --git a/cc/paint/paint_op_perftest.cc b/cc/paint/paint_op_perftest.cc
index 3369471..d061665 100644
--- a/cc/paint/paint_op_perftest.cc
+++ b/cc/paint/paint_op_perftest.cc
@@ -129,7 +129,7 @@
   SkScalar intervals[] = {1.f, 1.f};
   flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
   flags.setMaskFilter(SkMaskFilter::MakeBlur(
-      SkBlurStyle::kOuter_SkBlurStyle, 4.3f, SkRect::MakeXYWH(1, 1, 1, 1)));
+      SkBlurStyle::kOuter_SkBlurStyle, 4.3));
   flags.setColorFilter(
       SkColorMatrixFilter::MakeLightingFilter(SK_ColorYELLOW, SK_ColorGREEN));
 
diff --git a/cc/trees/layer_tree_host_pixeltest_tiles.cc b/cc/trees/layer_tree_host_pixeltest_tiles.cc
index eabe1909..8ed832e 100644
--- a/cc/trees/layer_tree_host_pixeltest_tiles.cc
+++ b/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -4,6 +4,7 @@
 
 #include <stddef.h>
 
+#include "build/build_config.h"
 #include "cc/layers/content_layer_client.h"
 #include "cc/layers/picture_layer.h"
 #include "cc/paint/display_item_list.h"
@@ -224,8 +225,16 @@
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png")));
 }
 
+// Flaky on Linux TSAN. https://crbug.com/707711
+#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#define MAYBE_PartialRaster_MultiThread_OneCopy \
+  DISABLED_PartialRaster_MultiThread_OneCopy
+#else
+#define MAYBE_PartialRaster_MultiThread_OneCopy \
+  PartialRaster_MultiThread_OneCopy
+#endif
 TEST_F(LayerTreeHostTilesTestPartialInvalidation,
-       PartialRaster_MultiThread_OneCopy) {
+       MAYBE_PartialRaster_MultiThread_OneCopy) {
   RunRasterPixelTest(
       true, PARTIAL_ONE_COPY, picture_layer_,
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_partial_flipped.png")));
diff --git a/chrome/VERSION b/chrome/VERSION
index cc47d2a..5ee5ed5 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=70
 MINOR=0
-BUILD=3530
+BUILD=3531
 PATCH=0
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 698faa1..c6e09028 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -29,8 +29,8 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.feed.action.FeedActionHandler;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.ContextMenuManager;
 import org.chromium.chrome.browser.ntp.ContextMenuManager.TouchEnabledDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPage;
diff --git a/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 1fdf5b39..4a0de26 100644
--- a/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.feed;
 
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 
diff --git a/chrome/android/java/res/layout/consent_bump_more_options_view.xml b/chrome/android/java/res/layout/consent_bump_more_options_view.xml
index a2868be4..e66720b 100644
--- a/chrome/android/java/res/layout/consent_bump_more_options_view.xml
+++ b/chrome/android/java/res/layout/consent_bump_more_options_view.xml
@@ -41,7 +41,7 @@
                 android:layout_marginStart="@dimen/signin_margin_start"
                 android:layout_marginTop="15dp"
                 android:layout_marginEnd="@dimen/signin_margin_end"
-                android:text="@string/consent_bump_title"
+                android:text="@string/consent_bump_more_options_title"
                 android:textAppearance="@style/BlackHeadline1"/>
             <TextView
                 android:id="@+id/consent_bump_description"
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 50aee3c7..97b1eda1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -95,8 +95,8 @@
 import org.chromium.chrome.browser.modaldialog.TabModalLifetimeHandler;
 import org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.native_page.NativePageAssassin;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.ntp.NativePageAssassin;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.omaha.OmahaBase;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
index 6fee936..c0c80db1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManager.java
@@ -19,10 +19,10 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
 import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
 import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksReader;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
index 6db90443..052cbd195 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java
@@ -8,10 +8,10 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
index 81e3821..9edc6a19 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/content/TabContentManager.java
@@ -15,7 +15,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.NativePage;
+import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.display.DisplayAndroid;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
index 8cc23d3..702767f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadPage.java
@@ -11,12 +11,12 @@
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.download.home.DownloadManagerCoordinator;
 import org.chromium.chrome.browser.download.home.DownloadManagerCoordinatorFactory;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index abfda48..7eaa9984 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -29,7 +29,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.download.DirectoryOption;
@@ -39,6 +38,7 @@
 import org.chromium.chrome.browser.download.home.DownloadManagerCoordinator;
 import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.download.DownloadPreferences;
 import org.chromium.chrome.browser.profiles.Profile;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index 8c65e81..51082b55 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -9,10 +9,10 @@
 import android.view.ViewGroup;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 
 /**
  * Provides functionality when the user interacts with the explore sites page.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
index 30867a4..44114e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.signin.AccountManagementFragment;
 import org.chromium.chrome.browser.signin.SigninManager;
 import org.chromium.chrome.browser.signin.SigninManager.SignInCallback;
+import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 
 /**
@@ -86,6 +87,9 @@
         signinManager.signIn(accountName, activity, new SignInCallback() {
             @Override
             public void onSignInComplete() {
+                if (ChromeFeatureList.isEnabled(ChromeFeatureList.UNIFIED_CONSENT)) {
+                    UnifiedConsentServiceBridge.setUnifiedConsentGiven(true);
+                }
                 // Show sync settings if user pressed the "Settings" button.
                 if (setUp) {
                     openSignInSettings(activity);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
index 2f616300..2fd30c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java
@@ -9,10 +9,10 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarManageable;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BasicNativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/BasicNativePage.java
rename to chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java
index 410734f..19e86f7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BasicNativePage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser;
+package org.chromium.chrome.browser.native_page;
 
 import android.app.Activity;
 import android.content.res.Resources;
@@ -11,6 +11,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.util.ColorUtils;
@@ -114,7 +115,7 @@
     }
 
     /**
-     * Updates the top and bottom margin depending on wether the browser controls are shown or
+     * Updates the top and bottom margin depending on whether the browser controls are shown or
      * hidden.
      */
     private void updateMargins(@BrowserControlsState int constraints) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/FrozenNativePage.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java
rename to chrome/android/java/src/org/chromium/chrome/browser/native_page/FrozenNativePage.java
index 5e3d5d08..d25172e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/FrozenNativePage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/FrozenNativePage.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser;
+package org.chromium.chrome.browser.native_page;
 
 import android.view.View;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePage.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
rename to chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePage.java
index 792d349d..dd7f761f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePage.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser;
+package org.chromium.chrome.browser.native_page;
 
 import android.view.View;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageAssassin.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java
similarity index 95%
rename from chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageAssassin.java
rename to chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java
index 59a4bbbb..ab32e083 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageAssassin.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java
@@ -2,10 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.ntp;
+package org.chromium.chrome.browser.native_page;
 
-import org.chromium.chrome.browser.FrozenNativePage;
-import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.tab.Tab;
 
 import java.lang.ref.WeakReference;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
rename to chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 1aeb6cf0..2869af3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.ntp;
+package org.chromium.chrome.browser.native_page;
 
 import android.net.Uri;
 import android.support.annotation.IntDef;
@@ -10,8 +10,6 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.NativePage;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.bookmarks.BookmarkPage;
@@ -19,6 +17,10 @@
 import org.chromium.chrome.browser.explore_sites.ExploreSitesPage;
 import org.chromium.chrome.browser.feed.FeedNewTabPage;
 import org.chromium.chrome.browser.history.HistoryPage;
+import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.ntp.RecentTabsManager;
+import org.chromium.chrome.browser.ntp.RecentTabsPage;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NativePageHost.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/NativePageHost.java
rename to chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java
index e248417..ea8d2ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/NativePageHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageHost.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser;
+package org.chromium.chrome.browser.native_page;
 
 import android.support.annotation.Nullable;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/native_page/OWNERS
new file mode 100644
index 0000000..ce4d924
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/OWNERS
@@ -0,0 +1,4 @@
+twellington@chromium.org
+mdjones@chromium.org
+
+#COMPONENT: UI>Browser>Mobile
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
index fd65950..2b29330f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoNewTabPage.java
@@ -13,13 +13,13 @@
 
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.BasicNativePage;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.native_page.BasicNativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.IncognitoNewTabPageView.IncognitoNewTabPageManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index ad50fff..3032049 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -25,11 +25,11 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePage;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
 import org.chromium.chrome.browser.download.DownloadManagerService;
+import org.chromium.chrome.browser.native_page.NativePage;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.omnibox.LocationBarVoiceRecognitionHandler;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
index 4c0a4142..48941e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
@@ -22,9 +22,9 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
+import org.chromium.chrome.browser.native_page.NativePage;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.ViewUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 4117799..fc7045ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -49,10 +49,10 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.WindowDelegate;
 import org.chromium.chrome.browser.locale.LocaleManager;
-import org.chromium.chrome.browser.ntp.NativePageFactory;
+import org.chromium.chrome.browser.native_page.NativePage;
+import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.ntp.NewTabPage.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
index 221ef4e..0a4e93f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/SyncAndServicesPreferences.java
@@ -42,6 +42,7 @@
 import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.SigninManager;
+import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.sync.GoogleServiceAuthError;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
@@ -318,10 +319,12 @@
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         String key = preference.getKey();
         if (USE_SYNC_AND_ALL_SERVICES.equals(key)) {
-            if ((boolean) newValue) {
+            boolean enabled = (boolean) newValue;
+            if (enabled) {
                 mSyncGroup.setExpanded(true);
                 mNonpersonalizedServices.setExpanded(true);
             }
+            UnifiedConsentServiceBridge.setUnifiedConsentGiven(enabled);
             ThreadUtils.postOnUiThread(this::updateSyncStateFromSelectedModelTypes);
             ThreadUtils.postOnUiThread(this::updateDataTypeState);
         } else if (PREF_SEARCH_SUGGESTIONS.equals(key)) {
@@ -778,6 +781,8 @@
     }
 
     private void updatePreferences() {
+        mUseSyncAndAllServices.setChecked(UnifiedConsentServiceBridge.isUnifiedConsentGiven());
+
         mNavigationError.setChecked(mPrefServiceBridge.isResolveNavigationErrorEnabled());
         mNetworkPredictions.setChecked(mPrefServiceBridge.getNetworkPredictionEnabled());
         mSearchSuggestions.setChecked(mPrefServiceBridge.isSearchSuggestEnabled());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java
index 931f713d..13cf440 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConsentBumpFragment.java
@@ -53,7 +53,7 @@
     @Override
     protected void onSigninAccepted(String accountName, boolean isDefaultAccount,
             boolean settingsClicked, Runnable callback) {
-        // TODO(https://crbug.com/869426): Save the consent state.
+        UnifiedConsentServiceBridge.setUnifiedConsentGiven(true);
         if (settingsClicked) {
             PreferencesLauncher.launchSettingsPage(
                     getActivity(), SyncAndServicesPreferences.class.getName());
@@ -64,6 +64,12 @@
 
     @Override
     @StringRes
+    protected int getTitleTextId() {
+        return R.string.consent_bump_title;
+    }
+
+    @Override
+    @StringRes
     protected int getNegativeButtonTextId() {
         return R.string.consent_bump_more_options;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
index 3bafafe..32ff9ac0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
@@ -134,6 +134,7 @@
         SigninManager.get().signIn(accountName, getActivity(), new SigninManager.SignInCallback() {
             @Override
             public void onSignInComplete() {
+                UnifiedConsentServiceBridge.setUnifiedConsentGiven(true);
                 if (settingsClicked) {
                     PreferencesLauncher.launchSettingsPage(
                             getActivity(), SyncAndServicesPreferences.class.getName());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
index ee11ab84..8010f64d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragmentBase.java
@@ -193,6 +193,15 @@
             boolean settingsClicked, Runnable callback);
 
     /**
+     * Returns the string resource id for the title TextView. This is invoked once from
+     * {@link #onCreateView}. Subclasses may override this method to customize the title text.
+     */
+    @StringRes
+    protected int getTitleTextId() {
+        return R.string.signin_title;
+    }
+
+    /**
      * Returns the string resource id for the negative button. This is invoked once from
      * {@link #onCreateView}.
      */
@@ -333,7 +342,7 @@
 
     /** Sets texts for immutable elements. Accept button text is set by {@link #setHasAccounts}. */
     private void updateConsentText() {
-        mConsentTextTracker.setText(mView.getTitleView(), R.string.signin_title);
+        mConsentTextTracker.setText(mView.getTitleView(), getTitleTextId());
         mConsentTextTracker.setText(
                 mView.getSyncDescriptionView(), R.string.signin_sync_description);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/UnifiedConsentServiceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/UnifiedConsentServiceBridge.java
index fd881fc..2d4cee7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/UnifiedConsentServiceBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/UnifiedConsentServiceBridge.java
@@ -12,6 +12,16 @@
 public class UnifiedConsentServiceBridge {
     private UnifiedConsentServiceBridge() {}
 
+    /** Sets whether the user has given unified consent. */
+    public static void setUnifiedConsentGiven(boolean unifiedConsentGiven) {
+        nativeSetUnifiedConsentGiven(Profile.getLastUsedProfile(), unifiedConsentGiven);
+    }
+
+    /** Returns whether the user has given unified consent. */
+    public static boolean isUnifiedConsentGiven() {
+        return nativeIsUnifiedConsentGiven(Profile.getLastUsedProfile());
+    }
+
     /**
      * Returns whether the consent bump should be shown as part of the migration to Unified Consent.
      */
@@ -19,5 +29,8 @@
         return nativeShouldShowConsentBump(Profile.getLastUsedProfile());
     }
 
+    private static native void nativeSetUnifiedConsentGiven(Profile profile, boolean consentGiven);
+    private static native boolean nativeIsUnifiedConsentGiven(Profile lastUsedProfile);
+
     private static native boolean nativeShouldShowConsentBump(Profile profile);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java
index 09df173b..5da2c6d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java
@@ -14,9 +14,9 @@
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.Promise;
 import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
 import org.chromium.chrome.browser.ntp.snippets.FaviconFetchResult;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
index b8cabc5..0ee1764 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
@@ -10,13 +10,13 @@
 import org.chromium.blink_public.web.WebReferrerPolicy;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
 import org.chromium.chrome.browser.device.DeviceClassManager;
 import org.chromium.chrome.browser.download.DownloadMetrics;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.ntp.NewTabPageUma;
 import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java
index 695e68e..e6d83c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java
@@ -7,7 +7,7 @@
 import android.support.annotation.Nullable;
 
 import org.chromium.base.DiscardableReferencePool;
-import org.chromium.chrome.browser.NativePageHost;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index b200f6e..902bd76 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -46,10 +46,8 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.ChromeVersionInfo;
-import org.chromium.chrome.browser.FrozenNativePage;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
-import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.SwipeRefreshHandler;
 import org.chromium.chrome.browser.TabState;
 import org.chromium.chrome.browser.TabState.WebContentsState;
@@ -72,9 +70,11 @@
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.media.ui.MediaSessionTabHelper;
+import org.chromium.chrome.browser.native_page.FrozenNativePage;
+import org.chromium.chrome.browser.native_page.NativePage;
+import org.chromium.chrome.browser.native_page.NativePageAssassin;
+import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.ntp.NativePageAssassin;
-import org.chromium.chrome.browser.ntp.NativePageFactory;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.policy.PolicyAuditor;
 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
index f4dd92fa..de3ac47 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/CustomTabToolbar.java
@@ -46,7 +46,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.WindowDelegate;
 import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper;
-import org.chromium.chrome.browser.ntp.NativePageFactory;
+import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.omnibox.UrlBar;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index c9eabe14..bde5f48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -33,7 +33,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
-import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.UrlConstants;
 import org.chromium.chrome.browser.WindowDelegate;
@@ -56,9 +55,10 @@
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
 import org.chromium.chrome.browser.metrics.OmniboxStartupMetrics;
+import org.chromium.chrome.browser.native_page.NativePage;
+import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.ntp.IncognitoNewTabPage;
-import org.chromium.chrome.browser.ntp.NativePageFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.omnibox.LocationBar;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
index 07ffdb66..f7fbe7a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarModel.java
@@ -22,7 +22,7 @@
 import org.chromium.chrome.browser.dom_distiller.DomDistillerServiceFactory;
 import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
 import org.chromium.chrome.browser.locale.LocaleManager;
-import org.chromium.chrome.browser.ntp.NativePageFactory;
+import org.chromium.chrome.browser.native_page.NativePageFactory;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.omnibox.AutocompleteController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index abc60bf..d97aa39 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -32,10 +32,10 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.TabLoadStatus;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 67f7f22..ce32e6ab 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2597,10 +2597,13 @@
         Choose an account
       </message>
       <!-- Consent bump screen strings -->
+      <message name="IDS_CONSENT_BUMP_TITLE" desc="Text for the title of the basic consent bump screen. This screen describes the user personalized and non-personalized features that depend on Unified Consent.">
+        Get more Google smarts
+      </message>
       <message name="IDS_CONSENT_BUMP_MORE_OPTIONS" desc="Text for the button in the basic consent bump screen that opens the advanced consent bump screen. The advanced consent bump screen lets the user select from several options to enable or customize personalized and non-personalized services. [CHAR-LIMIT=20]">
         More options
       </message>
-      <message name="IDS_CONSENT_BUMP_TITLE" desc="Text for the title of the consent bump screen with advanced options. This screen lets user select from several options to enable or customize personalized and non-personalized services.">
+      <message name="IDS_CONSENT_BUMP_MORE_OPTIONS_TITLE" desc="Text for the title of the consent bump screen with advanced options. This screen lets user select from several options to enable or customize personalized and non-personalized services.">
         Control sync, personalization, and more
       </message>
       <message name="IDS_CONSENT_BUMP_DESCRIPTION" desc="Text that explains why the consent bump screen has been shown.">
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index bbaa30b8..9fa55ce 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -18,7 +18,6 @@
   "java/src/org/chromium/chrome/browser/ApplicationLifetime.java",
   "java/src/org/chromium/chrome/browser/AssistStatusHandler.java",
   "java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java",
-  "java/src/org/chromium/chrome/browser/BasicNativePage.java",
   "java/src/org/chromium/chrome/browser/BitmapCache.java",
   "java/src/org/chromium/chrome/browser/BluetoothChooserDialog.java",
   "java/src/org/chromium/chrome/browser/BrowserRestartActivity.java",
@@ -42,7 +41,6 @@
   "java/src/org/chromium/chrome/browser/DeferredStartupHandler.java",
   "java/src/org/chromium/chrome/browser/DevToolsServer.java",
   "java/src/org/chromium/chrome/browser/FileProviderHelper.java",
-  "java/src/org/chromium/chrome/browser/FrozenNativePage.java",
   "java/src/org/chromium/chrome/browser/FullscreenActivity.java",
   "java/src/org/chromium/chrome/browser/InsetObserverView.java",
   "java/src/org/chromium/chrome/browser/IntentHandler.java",
@@ -55,8 +53,6 @@
   "java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java",
   "java/src/org/chromium/chrome/browser/LoginPrompt.java",
   "java/src/org/chromium/chrome/browser/LollipopTtsPlatformImpl.java",
-  "java/src/org/chromium/chrome/browser/NativePage.java",
-  "java/src/org/chromium/chrome/browser/NativePageHost.java",
   "java/src/org/chromium/chrome/browser/NavigationBarColorController.java",
   "java/src/org/chromium/chrome/browser/NavigationPopup.java",
   "java/src/org/chromium/chrome/browser/NearOomMonitor.java",
@@ -843,6 +839,12 @@
   "java/src/org/chromium/chrome/browser/mojo/ChromeInterfaceRegistrar.java",
   "java/src/org/chromium/chrome/browser/multiwindow/MultiInstanceChromeTabbedActivity.java",
   "java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java",
+  "java/src/org/chromium/chrome/browser/native_page/BasicNativePage.java",
+  "java/src/org/chromium/chrome/browser/native_page/FrozenNativePage.java",
+  "java/src/org/chromium/chrome/browser/native_page/NativePage.java",
+  "java/src/org/chromium/chrome/browser/native_page/NativePageAssassin.java",
+  "java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java",
+  "java/src/org/chromium/chrome/browser/native_page/NativePageHost.java",
   "java/src/org/chromium/chrome/browser/net/spdyproxy/DataReductionProxySettings.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamCallback.java",
   "java/src/org/chromium/chrome/browser/nfc/BeamController.java",
@@ -878,8 +880,6 @@
   "java/src/org/chromium/chrome/browser/ntp/LogoBridge.java",
   "java/src/org/chromium/chrome/browser/ntp/LogoDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/ntp/LogoView.java",
-  "java/src/org/chromium/chrome/browser/ntp/NativePageAssassin.java",
-  "java/src/org/chromium/chrome/browser/ntp/NativePageFactory.java",
   "java/src/org/chromium/chrome/browser/ntp/NativePageRootFrameLayout.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPage.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java",
@@ -2197,10 +2197,10 @@
   "junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java",
   "junit/src/org/chromium/chrome/browser/modelutil/PropertyModelTest.java",
   "junit/src/org/chromium/chrome/browser/modelutil/SimpleListObservableTest.java",
+  "junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationSystemStatusUtilUnitTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeUnitTest.java",
   "junit/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitionsTest.java",
-  "junit/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java",
   "junit/src/org/chromium/chrome/browser/ntp/TitleUtilTest.java",
   "junit/src/org/chromium/chrome/browser/ntp/cards/ContentSuggestionsUnitTestUtils.java",
   "junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
index a34b911..d7a2998 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -27,6 +27,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.R;
@@ -162,6 +163,7 @@
     @Test
     @MediumTest
     @Feature({"NewTabPage"})
+    @FlakyTest(message = "crbug.com/875544")
     public void testClickSuggestion() throws InterruptedException {
         setSuggestionsAndWaitForUpdate(10);
         List<SnippetArticle> suggestions = mSource.getSuggestionsForCategory(TEST_CATEGORY);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java
index 354e1da..8cdb14a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrTestRuleUtils.java
@@ -12,6 +12,7 @@
 import org.junit.runners.model.Statement;
 
 import org.chromium.base.test.params.ParameterSet;
+import org.chromium.chrome.browser.vr.VrFeedbackStatus;
 import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule;
 import org.chromium.chrome.browser.vr.rules.CustomTabActivityVrTestRule;
 import org.chromium.chrome.browser.vr.rules.VrTestRule;
@@ -56,6 +57,17 @@
         // IllegalStateException for being used from a backgrounded app.
         VrSettingsServiceUtils.checkForAndApplyVrSettingsFileAnnotation(desc, rule);
 
+        // Reset the VR feedback shared preferences if they're not currently the default because
+        // otherwise we can run into issues with VrFeedbackInfoBarTest#* erroneously failing due to
+        // tests being non-hermetic. Only set if not the default instead of unconditionally in order
+        // to avoid unnecessary disk writes.
+        if (VrFeedbackStatus.getFeedbackOptOut()) {
+            VrFeedbackStatus.setFeedbackOptOut(false);
+        }
+        if (VrFeedbackStatus.getUserExitedAndEntered2DCount() != 0) {
+            VrFeedbackStatus.setUserExitedAndEntered2DCount(0);
+        }
+
         try {
             base.evaluate();
         } finally {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java
similarity index 97%
rename from chrome/android/junit/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java
index 34ab405a..b0172be8 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NativePageFactoryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/native_page/NativePageFactoryTest.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.ntp;
+package org.chromium.chrome.browser.native_page;
 
 import android.view.View;
 
@@ -14,9 +14,8 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.NativePage;
 import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.ntp.NativePageFactory.NativePageType;
+import org.chromium.chrome.browser.native_page.NativePageFactory.NativePageType;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/native_page/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/native_page/OWNERS
new file mode 100644
index 0000000..57712089
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/native_page/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/native_page/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java
index fe45a5ef..ce46631 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java
@@ -23,11 +23,11 @@
 import org.chromium.base.Callback;
 import org.chromium.base.DiscardableReferencePool;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.NativePageHost;
 import org.chromium.chrome.browser.favicon.FaviconHelper;
 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
 import org.chromium.chrome.browser.favicon.LargeIconBridge;
 import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
+import org.chromium.chrome.browser.native_page.NativePageHost;
 import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
 import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
diff --git a/chrome/browser/android/signin/unified_consent_service_bridge.cc b/chrome/browser/android/signin/unified_consent_service_bridge.cc
index 1a577508..16e34fa6 100644
--- a/chrome/browser/android/signin/unified_consent_service_bridge.cc
+++ b/chrome/browser/android/signin/unified_consent_service_bridge.cc
@@ -10,6 +10,27 @@
 
 using base::android::JavaParamRef;
 
+static jboolean JNI_UnifiedConsentServiceBridge_IsUnifiedConsentGiven(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jclass>& jcaller,
+    const base::android::JavaParamRef<jobject>& profileAndroid) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(profileAndroid);
+  auto* unifiedConsentService =
+      UnifiedConsentServiceFactory::GetForProfile(profile);
+  return unifiedConsentService->IsUnifiedConsentGiven();
+}
+
+static void JNI_UnifiedConsentServiceBridge_SetUnifiedConsentGiven(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jclass>& jcaller,
+    const base::android::JavaParamRef<jobject>& profileAndroid,
+    jboolean unifiedConsentGiven) {
+  Profile* profile = ProfileAndroid::FromProfileAndroid(profileAndroid);
+  auto* unifiedConsentService =
+      UnifiedConsentServiceFactory::GetForProfile(profile);
+  unifiedConsentService->SetUnifiedConsentGiven(unifiedConsentGiven);
+}
+
 static jboolean JNI_UnifiedConsentServiceBridge_ShouldShowConsentBump(
     JNIEnv* env,
     const base::android::JavaParamRef<jclass>& jcaller,
diff --git a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc
index b471c5f..ea226155 100644
--- a/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc
+++ b/chrome/browser/bitmap_fetcher/bitmap_fetcher_service.cc
@@ -24,13 +24,13 @@
 // for few images like weather answers, but with rich entity suggestions showing
 // several images at once, even changing some while the user types, a larger
 // cache is necessary to avoid flickering. Each cache entry is expected to take
-// 16kb (64x64 @ 32bpp), and experimentation shows 18 entries is enough to
-// eliminate flicker with the standard 6 suggestion omnibox filled with entities
-// so the maximum expected memory consumption is ~288kb per browser window.
+// 16kb (64x64 @ 32bpp).  With 12, the total memory consumed would be ~192kb.
+// 12 is double the default number of maximum suggestions so this can
+// accommodate one match image plus one answer image for each result.
 #if defined(OS_ANDROID)
 const int kMaxCacheEntries = 5;
 #else
-const int kMaxCacheEntries = 18;
+const int kMaxCacheEntries = 12;
 #endif
 
 }  // namespace.
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index b5a1afb0..8533893 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -32,6 +32,8 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_oobe_configuration_client.h"
 #include "components/account_id/account_id.h"
 #include "components/arc/arc_prefs.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
@@ -1009,14 +1011,17 @@
 
 class ArcOobeTest : public ChromeArcUtilTest {
  public:
-  ArcOobeTest()
-      : oobe_configuration_(std::make_unique<chromeos::OobeConfiguration>()) {}
+  ArcOobeTest() {
+    chromeos::DBusThreadManager::GetSetterForTesting();
+    oobe_configuration_ = std::make_unique<chromeos::OobeConfiguration>();
+  }
 
   ~ArcOobeTest() override {
     // Fake display host have to be shut down first, as it may access
     // configuration.
     fake_login_display_host_.reset();
     oobe_configuration_.reset();
+    chromeos::DBusThreadManager::Shutdown();
   }
 
  protected:
diff --git a/chrome/browser/chromeos/file_manager/arc_file_tasks.cc b/chrome/browser/chromeos/file_manager/arc_file_tasks.cc
index 76b5c92..a798cdb4 100644
--- a/chrome/browser/chromeos/file_manager/arc_file_tasks.cc
+++ b/chrome/browser/chromeos/file_manager/arc_file_tasks.cc
@@ -221,9 +221,10 @@
   if (chromeos::ProfileHelper::IsPrimaryProfile(profile)) {
     auto* arc_service_manager = arc::ArcServiceManager::Get();
     if (arc_service_manager) {
+      // TODO(niwa): Switch to FileSystemInstance.OpenUrlsWithPermission().
       arc_intent_helper = ARC_GET_INSTANCE_FOR_METHOD(
           arc_service_manager->arc_bridge_service()->intent_helper(),
-          HandleUrlList);
+          HandleUrlListDeprecated);
     }
   }
   if (!arc_intent_helper) {
@@ -245,7 +246,7 @@
     urls.push_back(std::move(url_with_type));
   }
 
-  arc_intent_helper->HandleUrlList(
+  arc_intent_helper->HandleUrlListDeprecated(
       std::move(urls), AppIdToActivityName(task.app_id),
       FileTaskActionIdToArcActionType(task.action_id));
   done.Run(extensions::api::file_manager_private::TASK_RESULT_MESSAGE_SENT);
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index 075ab6e4..322dc7a 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -21,9 +21,11 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/assistant/buildflags.h"
 #include "chromeos/chromeos_switches.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
@@ -133,6 +135,15 @@
       }
     }
 
+#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
+    // Launch Assistant OOBE flow if Assistant is enabled.
+    if (account_supported && chromeos::switches::IsAssistantEnabled()) {
+      chromeos::AssistantOptInDialog::Show();
+      delete this;
+      return;
+    }
+#endif
+
     // If voice interaction value prop needs to be shown, the tutorial will be
     // shown after the voice interaction OOBE flow.
     if (account_supported && arc::IsArcPlayStoreEnabledForProfile(profile_) &&
diff --git a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
index aefb35483..4d79e974 100644
--- a/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
+++ b/chrome/browser/chromeos/login/enterprise_enrollment_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "build/build_config.h"
 #include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
 #include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper.h"
 #include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper_impl.h"
@@ -359,8 +360,16 @@
 
 // Shows the enrollment screen and simulates an enrollment complete event. We
 // verify that the enrollmenth helper receives the correct auth code.
+// Flaky on MSAN. https://crbug.com/876362
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_TestAuthCodeGetsProperlyReceivedFromGaia \
+  DISABLED_TestAuthCodeGetsProperlyReceivedFromGaia
+#else
+#define MAYBE_TestAuthCodeGetsProperlyReceivedFromGaia \
+  TestAuthCodeGetsProperlyReceivedFromGaia
+#endif
 IN_PROC_BROWSER_TEST_F(EnterpriseEnrollmentTest,
-                       TestAuthCodeGetsProperlyReceivedFromGaia) {
+                       MAYBE_TestAuthCodeGetsProperlyReceivedFromGaia) {
   ShowEnrollmentScreen();
   ExpectEnrollmentCredentials();
   SubmitEnrollmentCredentials();
@@ -388,8 +397,16 @@
 
 // Shows the enrollment screen and simulates a successful enrollment. Verifies
 // that the success screen is then displayed.
+// Flaky on MSAN. https://crbug.com/876362
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_TestProperPageGetsLoadedOnEnrollmentSuccess \
+  DISABLED_TestProperPageGetsLoadedOnEnrollmentSuccess
+#else
+#define MAYBE_TestProperPageGetsLoadedOnEnrollmentSuccess \
+  TestProperPageGetsLoadedOnEnrollmentSuccess
+#endif
 IN_PROC_BROWSER_TEST_F(EnterpriseEnrollmentTest,
-                       TestProperPageGetsLoadedOnEnrollmentSuccess) {
+                       MAYBE_TestProperPageGetsLoadedOnEnrollmentSuccess) {
   ShowEnrollmentScreen();
   DisableAttributePromptUpdate();
   SubmitEnrollmentCredentials();
@@ -407,8 +424,16 @@
 // attribute prompt screen. Verifies the attribute prompt screen is displayed.
 // Verifies that the data the user enters into the attribute prompt screen is
 // received by the enrollment helper.
+// Flaky on MSAN. https://crbug.com/876362
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_TestAttributePromptPageGetsLoaded \
+  DISABLED_TestAttributePromptPageGetsLoaded
+#else
+#define MAYBE_TestAttributePromptPageGetsLoaded \
+  TestAttributePromptPageGetsLoaded
+#endif
 IN_PROC_BROWSER_TEST_F(EnterpriseEnrollmentTest,
-                       TestAttributePromptPageGetsLoaded) {
+                       MAYBE_TestAttributePromptPageGetsLoaded) {
   ShowEnrollmentScreen();
   ExpectAttributePromptUpdate();
   SubmitEnrollmentCredentials();
diff --git a/chrome/browser/chromeos/login/oobe_configuration.cc b/chrome/browser/chromeos/login/oobe_configuration.cc
index 87fb6ae..28ce87c 100644
--- a/chrome/browser/chromeos/login/oobe_configuration.cc
+++ b/chrome/browser/chromeos/login/oobe_configuration.cc
@@ -6,35 +6,13 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
-#include "base/task/post_task.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/oobe_configuration_client.h"
 
 namespace chromeos {
 
-namespace {
-
-std::unique_ptr<base::Value> LoadOOBEConfigurationFile(base::FilePath path) {
-  std::string configuration_data;
-  if (!base::ReadFileToString(path, &configuration_data)) {
-    DLOG(WARNING) << "Can't read OOBE Configuration";
-    return std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  }
-  int error_code, row, col;
-  std::string error_message;
-  auto value = base::JSONReader::ReadAndReturnError(
-      configuration_data, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS,
-      &error_code, &error_message, &row, &col);
-  if (!value || !value->is_dict()) {
-    LOG(ERROR) << "Error parsing OOBE configuration: " << error_message;
-    return std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
-  }
-  return value;
-}
-
-}  // namespace
-
 // static
 OobeConfiguration* OobeConfiguration::instance = nullptr;
 
@@ -74,23 +52,32 @@
     observer.OnOobeConfigurationChanged();
 }
 
-void OobeConfiguration::OnConfigurationLoaded(
-    std::unique_ptr<base::Value> configuration) {
-  LOG(ERROR) << " DEBUG OUT *****************OnConfigurationLoaded";
-  if (!configuration)
-    return;
-
-  configuration_ = std::move(configuration);
-  for (auto& observer : observer_list_)
-    observer.OnOobeConfigurationChanged();
+void OobeConfiguration::CheckConfiguration() {
+  DBusThreadManager::Get()
+      ->GetOobeConfigurationClient()
+      ->CheckForOobeConfiguration(
+          base::BindOnce(&OobeConfiguration::OnConfigurationCheck,
+                         weak_factory_.GetWeakPtr()));
 }
 
-void OobeConfiguration::LoadConfiguration(const base::FilePath& path) {
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
-      base::BindOnce(&LoadOOBEConfigurationFile, path),
-      base::BindOnce(&OobeConfiguration::OnConfigurationLoaded,
-                     weak_factory_.GetWeakPtr()));
+void OobeConfiguration::OnConfigurationCheck(bool has_configuration,
+                                             const std::string& configuration) {
+  if (!has_configuration)
+    return;
+
+  int error_code, row, col;
+  std::string error_message;
+  auto value = base::JSONReader::ReadAndReturnError(
+      configuration, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS,
+      &error_code, &error_message, &row, &col);
+  if (!value || !value->is_dict()) {
+    LOG(ERROR) << "Error parsing OOBE configuration: " << error_message;
+    return;
+  }
+
+  configuration_ = std::move(value);
+  for (auto& observer : observer_list_)
+    observer.OnOobeConfigurationChanged();
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/oobe_configuration.h b/chrome/browser/chromeos/login/oobe_configuration.h
index 05eb14f5..196dd46a 100644
--- a/chrome/browser/chromeos/login/oobe_configuration.h
+++ b/chrome/browser/chromeos/login/oobe_configuration.h
@@ -36,14 +36,15 @@
   void RemoveObserver(Observer* observer);
 
   void ResetConfiguration();
-  void LoadConfiguration(const base::FilePath& path);
+  void CheckConfiguration();
 
   // Returns current OobeConfiguration instance and NULL if it hasn't been
   // initialized yet.
   static OobeConfiguration* Get();
 
  private:
-  void OnConfigurationLoaded(std::unique_ptr<base::Value> configuration);
+  void OnConfigurationCheck(bool has_configuration,
+                            const std::string& configuration);
 
   // Pointer to the existing OobeConfiguration instance (if any).
   // Set in ctor, reset in dtor. Not owned since we need to control the lifetime
diff --git a/chrome/browser/chromeos/login/session/chrome_session_manager.cc b/chrome/browser/chromeos/login/session/chrome_session_manager.cc
index 5d58836..5b8afb0 100644
--- a/chrome/browser/chromeos/login/session/chrome_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/chrome_session_manager.cc
@@ -222,11 +222,11 @@
   if (parsed_command_line.HasSwitch(switches::kLoginManager) &&
       (!is_running_test || force_login_screen_in_test)) {
     VLOG(1) << "Starting Chrome with login/oobe screen.";
-    LoadOobeConfiguration();
+    oobe_configuration_->CheckConfiguration();
     StartLoginOobeSession();
     return;
   } else if (is_running_test) {
-    LoadOobeConfiguration();
+    oobe_configuration_->CheckConfiguration();
   }
 
   if (!base::SysInfo::IsRunningOnChromeOS() &&
@@ -269,21 +269,10 @@
   chromeos::WebUIScreenLocker::RequestPreload();
 }
 
-void ChromeSessionManager::LoadOobeConfiguration() {
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          chromeos::switches::kOobeConfiguration))
-    return;
-  VLOG(1) << "Loading OOBE configuration ";
-  oobe_configuration_->LoadConfiguration(
-      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
-          chromeos::switches::kOobeConfiguration));
-}
-
 void ChromeSessionManager::NotifyUserLoggedIn(const AccountId& user_account_id,
                                               const std::string& user_id_hash,
                                               bool browser_restart,
                                               bool is_child) {
-  oobe_configuration_->ResetConfiguration();
   BootTimesRecorder* btl = BootTimesRecorder::Get();
   btl->AddLoginTimeMarker("UserLoggedIn-Start", false);
   session_manager::SessionManager::NotifyUserLoggedIn(
diff --git a/chrome/browser/chromeos/login/session/chrome_session_manager.h b/chrome/browser/chromeos/login/session/chrome_session_manager.h
index 2a0caea..bb3fe5fb 100644
--- a/chrome/browser/chromeos/login/session/chrome_session_manager.h
+++ b/chrome/browser/chromeos/login/session/chrome_session_manager.h
@@ -42,7 +42,6 @@
                           bool is_child) override;
 
  private:
-  void LoadOobeConfiguration();
   std::unique_ptr<chromeos::OobeConfiguration> oobe_configuration_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeSessionManager);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 7bcf647..a4830af 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -97,7 +97,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/logging_chrome.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/assistant/buildflags.h"
 #include "chromeos/cert_loader.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
@@ -148,10 +147,6 @@
 #include "components/rlz/rlz_tracker.h"
 #endif
 
-#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
-#include "chrome/browser/ui/ash/assistant/assistant_client.h"
-#endif
-
 namespace chromeos {
 
 namespace {
@@ -1541,13 +1536,6 @@
   ProfileHelper::Get()->ProfileStartup(profile);
 
   if (start_session_type_ == PRIMARY_USER_SESSION) {
-#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
-    // Initialize Assistant early to be used in post login Oobe steps.
-    if (chromeos::switches::IsAssistantEnabled()) {
-      AssistantClient::Get()->MaybeInit(
-          content::BrowserContext::GetConnectorFor(profile));
-    }
-#endif
     UserFlow* user_flow = ChromeUserManager::Get()->GetCurrentUserFlow();
     WizardController* oobe_controller = WizardController::default_controller();
     base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
diff --git a/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc b/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc
index 7bfbcdb..4e4556a 100644
--- a/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc
+++ b/chrome/browser/chromeos/login/users/multi_profile_user_controller_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
@@ -28,6 +29,7 @@
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/cert/cert_verify_proc.h"
 #include "net/cert/x509_certificate.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
@@ -116,6 +118,28 @@
       user_manager::UserManager::Get());
 }
 
+class MockCertVerifyProc : public net::CertVerifyProc {
+ public:
+  MockCertVerifyProc() = default;
+
+  // net::CertVerifyProc implementation
+  bool SupportsAdditionalTrustAnchors() const override { return true; }
+
+ protected:
+  ~MockCertVerifyProc() override = default;
+
+ private:
+  int VerifyInternal(net::X509Certificate* cert,
+                     const std::string& hostname,
+                     const std::string& ocsp_response,
+                     int flags,
+                     net::CRLSet* crl_set,
+                     const net::CertificateList& additional_trust_anchors,
+                     net::CertVerifyResult* result) override {
+    return net::ERR_FAILED;
+  }
+};
+
 }  // namespace
 
 class MultiProfileUserControllerTest
@@ -386,6 +410,8 @@
   LoginUser(0);
 
   cert_verifier_.reset(new policy::PolicyCertVerifier(base::Closure()));
+  cert_verifier_->InitializeOnIOThread(
+      base::MakeRefCounted<MockCertVerifyProc>());
   g_policy_cert_verifier_for_factory = cert_verifier_.get();
   ASSERT_TRUE(
       policy::PolicyCertServiceFactory::GetInstance()->SetTestingFactoryAndUse(
@@ -422,6 +448,8 @@
   SetPrefBehavior(0, MultiProfileUserController::kBehaviorUnrestricted);
 
   cert_verifier_.reset(new policy::PolicyCertVerifier(base::Closure()));
+  cert_verifier_->InitializeOnIOThread(
+      base::MakeRefCounted<MockCertVerifyProc>());
   g_policy_cert_verifier_for_factory = cert_verifier_.get();
   ASSERT_TRUE(
       policy::PolicyCertServiceFactory::GetInstance()->SetTestingFactoryAndUse(
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 607e768b..6f12b99 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -81,14 +81,12 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/tablet_mode_client.h"
-#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/assistant/buildflags.h"
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/chromeos_constants.h"
 #include "chromeos/chromeos_switches.h"
@@ -674,7 +672,7 @@
           arc::prefs::kArcTermsShownInOobe, true);
     }
   } else {
-    ShowAssistantOptIn();
+    ShowUserImageScreen();
   }
 }
 
@@ -792,25 +790,6 @@
   SetCurrentScreen(GetScreen(OobeScreen::SCREEN_DISCOVER));
 }
 
-void WizardController::ShowAssistantOptIn() {
-#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
-  if (chromeos::switches::IsAssistantEnabled()) {
-    DCHECK(!chromeos::AssistantOptInDialog::IsActive());
-    // TODO(updowndota) Refactor Assistant opt-in code to better fit into the
-    // oobe flow logic.
-    chromeos::AssistantOptInDialog::Show(
-        base::BindOnce(&WizardController::OnAssistantOptInCompleted,
-                       weak_factory_.GetWeakPtr()));
-  }
-#else
-  ShowUserImageScreen();
-#endif
-}
-
-void WizardController::OnAssistantOptInCompleted(bool accepted) {
-  ShowUserImageScreen();
-}
-
 void WizardController::SkipToLoginForTesting(
     const LoginScreenContext& context) {
   VLOG(1) << "SkipToLoginForTesting.";
@@ -1063,10 +1042,9 @@
     OnOobeFlowFinished();
     return;
   }
-
   // If the user finished with the PlayStore Terms of Service, advance to the
-  // assistant opt-in flow.
-  ShowAssistantOptIn();
+  // user image screen.
+  ShowUserImageScreen();
 }
 
 void WizardController::OnArcTermsOfServiceAccepted() {
@@ -1082,11 +1060,11 @@
 
   // If the feature flag for recommend app screen is on, show it after the user
   // finished with the PlayStore Terms of Service. Otherwise, advance to the
-  // assistant opt-in flow.
+  // user image screen.
   if (ShouldShowRecommendAppsScreen()) {
     ShowRecommendAppsScreen();
   } else {
-    ShowAssistantOptIn();
+    ShowUserImageScreen();
   }
 }
 
@@ -1097,7 +1075,7 @@
 }
 
 void WizardController::OnRecommendAppsSkipped() {
-  ShowAssistantOptIn();
+  ShowUserImageScreen();
 }
 
 void WizardController::OnRecommendAppsSelected() {
@@ -1105,7 +1083,7 @@
 }
 
 void WizardController::OnAppDownloadingFinished() {
-  ShowAssistantOptIn();
+  ShowUserImageScreen();
 }
 
 void WizardController::OnVoiceInteractionValuePropSkipped() {
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index 4d59d6f..cffe2e4 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -195,10 +195,6 @@
   void ShowUpdateRequiredScreen();
   void ShowDiscoverScreen();
 
-  // Shows assistant opt-in flow.
-  void ShowAssistantOptIn();
-  void OnAssistantOptInCompleted(bool accepted);
-
   // Shows images login screen.
   void ShowLoginScreen(const LoginScreenContext& context);
 
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index 62008285..edb83d0 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -64,6 +64,7 @@
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/chromeos_test_utils.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
 #include "chromeos/dbus/fake_shill_manager_client.h"
@@ -2510,7 +2511,7 @@
     ASSERT_TRUE(chromeos::test_utils::GetTestDataPath(
         "oobe_configuration", "non_empty_configuration.json",
         &configuration_file));
-    command_line->AppendSwitchPath(chromeos::switches::kOobeConfiguration,
+    command_line->AppendSwitchPath(chromeos::switches::kFakeOobeConfiguration,
                                    configuration_file);
   }
 
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.cc b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
index 86bca5e7..0512d74 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
@@ -39,6 +39,16 @@
   std::move(completion_callback).Run(error);
 }
 
+net::CertVerifier::Config ExtendTrustAnchors(
+    const net::CertVerifier::Config& config,
+    const net::CertificateList& trust_anchors) {
+  net::CertVerifier::Config new_config = config;
+  new_config.additional_trust_anchors.insert(
+      new_config.additional_trust_anchors.begin(), trust_anchors.begin(),
+      trust_anchors.end());
+  return new_config;
+}
+
 }  // namespace
 
 PolicyCertVerifier::PolicyCertVerifier(
@@ -60,12 +70,18 @@
   }
   delegate_ = std::make_unique<net::CachingCertVerifier>(
       std::make_unique<net::MultiThreadedCertVerifier>(verify_proc.get()));
+  delegate_->SetConfig(ExtendTrustAnchors(orig_config_, trust_anchors_));
 }
 
 void PolicyCertVerifier::SetTrustAnchors(
     const net::CertificateList& trust_anchors) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  if (trust_anchors == trust_anchors_)
+    return;
   trust_anchors_ = trust_anchors;
+  if (!delegate_)
+    return;
+  delegate_->SetConfig(ExtendTrustAnchors(orig_config_, trust_anchors_));
 }
 
 int PolicyCertVerifier::Verify(const RequestParams& params,
@@ -80,20 +96,16 @@
       base::BindOnce(&CompleteAndSignalAnchorUse, anchor_used_callback_,
                      std::move(completion_callback), verify_result);
 
-  net::CertificateList merged_trust_anchors(params.additional_trust_anchors());
-  merged_trust_anchors.insert(merged_trust_anchors.begin(),
-                              trust_anchors_.begin(), trust_anchors_.end());
-  net::CertVerifier::RequestParams new_params(
-      params.certificate(), params.hostname(), params.flags(),
-      params.ocsp_response(), merged_trust_anchors);
-  int error = delegate_->Verify(new_params, verify_result,
+  int error = delegate_->Verify(params, verify_result,
                                 std::move(wrapped_callback), out_req, net_log);
   MaybeSignalAnchorUse(error, anchor_used_callback_, *verify_result);
   return error;
 }
 
 void PolicyCertVerifier::SetConfig(const Config& config) {
-  delegate_->SetConfig(config);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  orig_config_ = config;
+  delegate_->SetConfig(ExtendTrustAnchors(orig_config_, trust_anchors_));
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.h b/chrome/browser/chromeos/policy/policy_cert_verifier.h
index 3abd7b8..ab952c4 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier.h
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier.h
@@ -52,6 +52,7 @@
   void SetConfig(const Config& config) override;
 
  private:
+  net::CertVerifier::Config orig_config_;
   net::CertificateList trust_anchors_;
   base::Closure anchor_used_callback_;
   std::unique_ptr<CertVerifier> delegate_;
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier_unittest.cc b/chrome/browser/chromeos/policy/policy_cert_verifier_unittest.cc
index 66418cf..1cc70f03 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier_unittest.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier_unittest.cc
@@ -76,11 +76,11 @@
       const net::TestCompletionCallback& test_callback,
       net::CertVerifyResult* verify_result,
       std::unique_ptr<net::CertVerifier::Request>* request) {
-    return cert_verifier_->Verify(net::CertVerifier::RequestParams(
-                                      test_server_cert_.get(), "127.0.0.1", 0,
-                                      std::string(), net::CertificateList()),
-                                  verify_result, test_callback.callback(),
-                                  request, net::NetLogWithSource());
+    return cert_verifier_->Verify(
+        net::CertVerifier::RequestParams(test_server_cert_.get(), "127.0.0.1",
+                                         0, std::string()),
+        verify_result, test_callback.callback(), request,
+        net::NetLogWithSource());
   }
 
   bool SupportsAdditionalTrustAnchors() {
@@ -235,7 +235,10 @@
     net::TestCompletionCallback callback;
     std::unique_ptr<net::CertVerifier::Request> request;
     int error = VerifyTestServerCert(callback, &verify_result, &request);
-    // Note: this hits the cached result from the first Verify() in this test.
+    // Note: Changing the trust anchors should flush the cache.
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request);
+    error = callback.WaitForResult();
     EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
   }
   // The additional trust anchors were reset, thus |cert_verifier_| should not
@@ -243,4 +246,58 @@
   EXPECT_FALSE(WasTrustAnchorUsedAndReset());
 }
 
+TEST_F(PolicyCertVerifierTest,
+       VerifyUsesAdditionalTrustAnchorsAfterConfigChange) {
+  ASSERT_TRUE(SupportsAdditionalTrustAnchors());
+
+  // |test_server_cert_| is untrusted, so Verify() fails.
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    std::unique_ptr<net::CertVerifier::Request> request;
+    int error = VerifyTestServerCert(callback, &verify_result, &request);
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request);
+    error = callback.WaitForResult();
+    EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
+  }
+  EXPECT_FALSE(WasTrustAnchorUsedAndReset());
+
+  net::CertificateList test_ca_x509cert_list =
+      net::x509_util::CreateX509CertificateListFromCERTCertificates(
+          test_ca_cert_list_);
+  ASSERT_FALSE(test_ca_x509cert_list.empty());
+
+  // Verify() again with the additional trust anchors.
+  cert_verifier_->SetTrustAnchors(test_ca_x509cert_list);
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    std::unique_ptr<net::CertVerifier::Request> request;
+    int error = VerifyTestServerCert(callback, &verify_result, &request);
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request);
+    error = callback.WaitForResult();
+    EXPECT_EQ(net::OK, error);
+  }
+  EXPECT_TRUE(WasTrustAnchorUsedAndReset());
+
+  // Change the configuration to enable SHA-1, which should still use the
+  // additional trust anchors.
+  net::CertVerifier::Config config;
+  config.enable_sha1_local_anchors = true;
+  cert_verifier_->SetConfig(config);
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    std::unique_ptr<net::CertVerifier::Request> request;
+    int error = VerifyTestServerCert(callback, &verify_result, &request);
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request);
+    error = callback.WaitForResult();
+    EXPECT_EQ(net::OK, error);
+  }
+  EXPECT_TRUE(WasTrustAnchorUsedAndReset());
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
index b422c30b..e578304 100644
--- a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory_browsertest.cc
@@ -96,7 +96,7 @@
   base::MessageLoopCurrent::ScopedNestableTaskAllower allow_nested;
   *verification_result = test_callback.GetResult(cert_verifier->Verify(
       net::CertVerifier::RequestParams(test_server_cert.get(), "127.0.0.1", 0,
-                                       std::string(), net::CertificateList()),
+                                       std::string()),
       &verify_result, test_callback.callback(), &request,
       net::NetLogWithSource()));
 }
diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn
index ee871a2..d6ca561 100644
--- a/chrome/browser/extensions/api/BUILD.gn
+++ b/chrome/browser/extensions/api/BUILD.gn
@@ -10,17 +10,15 @@
 assert(enable_extensions,
        "Cannot depend on extensions because enable_extensions=false.")
 
-json_schema_api("api_registration") {
-  sources = chrome_extensions_api_schema_sources
+function_registration("api_registration") {
+  sources = chrome_extensions_api_schema_sources +
+            chrome_extensions_api_uncompiled_sources
   impl_dir = "//chrome/browser/extensions/api"
-  bundle_registration = true
   configs = [ "//build/config:precompiled_headers" ]
   bundle_name = "Chrome"
   root_namespace = chrome_extensions_api_root_namespace
   schema_include_rules = chrome_extensions_api_schema_include_rules
 
-  uncompiled_sources = chrome_extensions_api_uncompiled_sources
-
   deps = [
     # Different APIs include headers from these targets.
     "//content/public/browser",
diff --git a/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc b/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
index b6ba68e..04e63f9 100644
--- a/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc
@@ -197,8 +197,7 @@
 
   const int return_value = verifier->Verify(
       net::CertVerifier::RequestParams(std::move(cert_chain), details.hostname,
-                                       flags, ocsp_response,
-                                       net::CertificateList()),
+                                       flags, ocsp_response),
       verify_result_ptr, bound_callback, &request_state->request, *net_log);
 
   if (return_value != net::ERR_IO_PENDING) {
diff --git a/chrome/browser/net/trial_comparison_cert_verifier.cc b/chrome/browser/net/trial_comparison_cert_verifier.cc
index 6e57f25..92d7ae5 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier.cc
@@ -324,9 +324,9 @@
       }
       // Chains were different, reverify the trial_result_.verified_cert chain
       // using the platform verifier and compare results again.
-      RequestParams reverification_params(
-          trial_result_.verified_cert, params_.hostname(), params_.flags(),
-          params_.ocsp_response(), params_.additional_trust_anchors());
+      RequestParams reverification_params(trial_result_.verified_cert,
+                                          params_.hostname(), params_.flags(),
+                                          params_.ocsp_response());
 
       int rv = cert_verifier_->primary_reverifier()->Verify(
           reverification_params, &reverification_result_,
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
index 8cb32fd..54bac50 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
@@ -299,9 +299,9 @@
       profile(),
       base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result),
       base::MakeRefCounted<NotCalledCertVerifyProc>());
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -337,9 +337,9 @@
       profile(),
       base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result),
       base::MakeRefCounted<NotCalledCertVerifyProc>());
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -375,9 +375,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -409,9 +409,9 @@
       profile()->GetOffTheRecordProfile(),  // Use an incognito Profile.
       base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result),
       base::MakeRefCounted<NotCalledCertVerifyProc>());
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -451,9 +451,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -519,9 +519,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -590,9 +590,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -660,9 +660,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -739,9 +739,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -843,8 +843,7 @@
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
   net::CertVerifier::RequestParams params(leaf, "test.example", 0 /* flags */,
-                                          std::string() /* ocsp_response */,
-                                          {} /* additional_trust_anchors */);
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -896,8 +895,7 @@
   verifier.SetConfig(config);
 
   net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0,
-                                          std::string() /* ocsp_response */,
-                                          {} /* additional_trust_anchors */);
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -975,9 +973,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
 
   // Start first verification request.
   net::CertVerifyResult result_1;
@@ -1061,9 +1059,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   std::unique_ptr<net::CertVerifier::Request> request;
   int error =
@@ -1129,9 +1127,9 @@
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
       profile(), verify_proc1, base::MakeRefCounted<NotCalledCertVerifyProc>());
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   std::unique_ptr<net::CertVerifier::Request> request;
   int error =
@@ -1179,9 +1177,9 @@
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
       profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1235,9 +1233,9 @@
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
       profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1310,9 +1308,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1370,9 +1368,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1437,9 +1435,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1518,9 +1516,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1580,9 +1578,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1650,9 +1648,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
@@ -1700,9 +1698,9 @@
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
 
-  net::CertVerifier::RequestParams params(
-      leaf_cert_1_, "127.0.0.1", 0 /* flags */,
-      std::string() /* ocsp_response */, {} /* additional_trust_anchors */);
+  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
+                                          0 /* flags */,
+                                          std::string() /* ocsp_response */);
   net::CertVerifyResult result;
   net::TestCompletionCallback callback;
   std::unique_ptr<net::CertVerifier::Request> request;
diff --git a/chrome/browser/notifications/persistent_notification_handler.cc b/chrome/browser/notifications/persistent_notification_handler.cc
index 6ec3506b..57a487cbd 100644
--- a/chrome/browser/notifications/persistent_notification_handler.cc
+++ b/chrome/browser/notifications/persistent_notification_handler.cc
@@ -18,6 +18,7 @@
 #include "content/public/browser/notification_event_dispatcher.h"
 #include "content/public/browser/permission_controller.h"
 #include "content/public/browser/permission_type.h"
+#include "content/public/common/persistent_notification_status.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
@@ -68,9 +69,7 @@
     base::OnceClosure completed_closure,
     content::PersistentNotificationStatus status) {
   UMA_HISTOGRAM_ENUMERATION(
-      "Notifications.PersistentWebNotificationCloseResult", status,
-      content::PersistentNotificationStatus::
-          PERSISTENT_NOTIFICATION_STATUS_MAX);
+      "Notifications.PersistentWebNotificationCloseResult", status);
 
   std::move(completed_closure).Run();
 }
@@ -88,6 +87,16 @@
   NotificationMetricsLogger* metrics_logger =
       NotificationMetricsLoggerFactory::GetForBrowserContext(profile);
 
+#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
+  // Ensure the browser stays alive while the event is processed. The keep alive
+  // will be reset when all click events have been acknowledged.
+  if (pending_click_dispatch_events_++ == 0) {
+    click_dispatch_keep_alive_ = std::make_unique<ScopedKeepAlive>(
+        KeepAliveOrigin::PENDING_NOTIFICATION_CLICK_EVENT,
+        KeepAliveRestartOption::DISABLED);
+  }
+#endif
+
   blink::mojom::PermissionStatus permission_status =
       content::BrowserContext::GetPermissionController(profile)
           ->GetPermissionStatus(content::PermissionType::NOTIFICATIONS, origin,
@@ -98,7 +107,8 @@
   if (permission_status != blink::mojom::PermissionStatus::GRANTED) {
     metrics_logger->LogPersistentNotificationClickWithoutPermission();
 
-    std::move(completed_closure).Run();
+    OnClickCompleted(profile, notification_id, std::move(completed_closure),
+                     content::PersistentNotificationStatus::kPermissionMissing);
     return;
   }
 
@@ -107,16 +117,6 @@
   else
     metrics_logger->LogPersistentNotificationClick();
 
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-  // Ensure the browser stays alive while the event is processed. The keep alive
-  // will be reset when all click events have been acknowledged.
-  if (pending_click_dispatch_events_++ == 0) {
-    click_dispatch_keep_alive_.reset(
-        new ScopedKeepAlive(KeepAliveOrigin::PENDING_NOTIFICATION_CLICK_EVENT,
-                            KeepAliveRestartOption::DISABLED));
-  }
-#endif
-
   // Notification clicks are considered a form of engagement with the |origin|,
   // thus we log the interaction with the Site Engagement service.
   SiteEngagementService::Get(profile)->HandleNotificationInteraction(origin);
@@ -125,17 +125,34 @@
       ->DispatchNotificationClickEvent(
           profile, notification_id, origin, action_index, reply,
           base::BindOnce(&PersistentNotificationHandler::OnClickCompleted,
-                         weak_ptr_factory_.GetWeakPtr(),
-                         std::move(completed_closure)));
+                         weak_ptr_factory_.GetWeakPtr(), profile,
+                         notification_id, std::move(completed_closure)));
 }
 
 void PersistentNotificationHandler::OnClickCompleted(
+    Profile* profile,
+    const std::string& notification_id,
     base::OnceClosure completed_closure,
     content::PersistentNotificationStatus status) {
   UMA_HISTOGRAM_ENUMERATION(
-      "Notifications.PersistentWebNotificationClickResult", status,
-      content::PersistentNotificationStatus::
-          PERSISTENT_NOTIFICATION_STATUS_MAX);
+      "Notifications.PersistentWebNotificationClickResult", status);
+
+  switch (status) {
+    case content::PersistentNotificationStatus::kSuccess:
+    case content::PersistentNotificationStatus::kServiceWorkerError:
+    case content::PersistentNotificationStatus::kWaitUntilRejected:
+      // There either wasn't a failure, or one that's in the developer's
+      // control, so we don't act on the origin's behalf.
+      break;
+    case content::PersistentNotificationStatus::kServiceWorkerMissing:
+    case content::PersistentNotificationStatus::kDatabaseError:
+    case content::PersistentNotificationStatus::kPermissionMissing:
+      // There was a failure that's out of the developer's control. The user now
+      // observes a stuck notification, so let's close it for them.
+      PlatformNotificationServiceImpl::GetInstance()
+          ->ClosePersistentNotification(profile, notification_id);
+      break;
+  }
 
 #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
   DCHECK_GT(pending_click_dispatch_events_, 0);
diff --git a/chrome/browser/notifications/persistent_notification_handler.h b/chrome/browser/notifications/persistent_notification_handler.h
index d6b3974..f9df19c 100644
--- a/chrome/browser/notifications/persistent_notification_handler.h
+++ b/chrome/browser/notifications/persistent_notification_handler.h
@@ -11,10 +11,13 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/notifications/notification_handler.h"
 #include "chrome/common/buildflags.h"
-#include "content/public/common/persistent_notification_status.h"
 
 class ScopedKeepAlive;
 
+namespace content {
+enum class PersistentNotificationStatus;
+}  // namespace content
+
 // NotificationHandler implementation for persistent Web Notifications, that is,
 // notifications associated with a Service Worker. Lives on the UI thread.
 class PersistentNotificationHandler : public NotificationHandler {
@@ -40,7 +43,9 @@
  private:
   void OnCloseCompleted(base::OnceClosure completed_closure,
                         content::PersistentNotificationStatus status);
-  void OnClickCompleted(base::OnceClosure completed_closure,
+  void OnClickCompleted(Profile* profile,
+                        const std::string& notification_id,
+                        base::OnceClosure completed_closure,
                         content::PersistentNotificationStatus status);
 
 #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
diff --git a/chrome/browser/notifications/persistent_notification_handler_unittest.cc b/chrome/browser/notifications/persistent_notification_handler_unittest.cc
index d1b7025b..e7e3aba 100644
--- a/chrome/browser/notifications/persistent_notification_handler_unittest.cc
+++ b/chrome/browser/notifications/persistent_notification_handler_unittest.cc
@@ -7,11 +7,16 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/run_loop.h"
 #include "chrome/browser/notifications/metrics/mock_notification_metrics_logger.h"
 #include "chrome/browser/notifications/metrics/notification_metrics_logger_factory.h"
+#include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/notifications/notification_permission_context.h"
+#include "chrome/browser/notifications/platform_notification_service_impl.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/permission_type.h"
+#include "content/public/common/notification_resources.h"
+#include "content/public/common/persistent_notification_status.h"
 #include "content/public/test/mock_permission_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -55,9 +60,12 @@
   DISALLOW_COPY_AND_ASSIGN(TestingProfileWithPermissionManager);
 };
 
+}  // namespace
+
 class PersistentNotificationHandlerTest : public ::testing::Test {
  public:
-  PersistentNotificationHandlerTest() : origin_(kExampleOrigin) {}
+  PersistentNotificationHandlerTest()
+      : display_service_tester_(&profile_), origin_(kExampleOrigin) {}
 
   ~PersistentNotificationHandlerTest() override = default;
 
@@ -67,11 +75,15 @@
         NotificationMetricsLoggerFactory::GetInstance()
             ->SetTestingFactoryAndUse(
                 &profile_, &MockNotificationMetricsLogger::FactoryForTests));
+
+    PlatformNotificationServiceImpl::GetInstance()
+        ->ClearClosedNotificationsForTesting();
   }
 
  protected:
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfileWithPermissionManager profile_;
+  NotificationDisplayServiceTester display_service_tester_;
 
   // The origin for which these tests are being run.
   GURL origin_;
@@ -96,6 +108,45 @@
                    base::DoNothing());
 }
 
+TEST_F(PersistentNotificationHandlerTest,
+       OnClick_CloseUnactionableNotifications) {
+  // Show a notification for a particular origin.
+  {
+    base::RunLoop run_loop;
+    display_service_tester_.SetNotificationAddedClosure(run_loop.QuitClosure());
+
+    EXPECT_CALL(*mock_logger_, LogPersistentNotificationShown());
+
+    PlatformNotificationServiceImpl::GetInstance()
+        ->DisplayPersistentNotification(&profile_, kExampleNotificationId,
+                                        origin_ /* service_worker_scope */,
+                                        origin_,
+                                        content::PlatformNotificationData(),
+                                        content::NotificationResources());
+
+    run_loop.Run();
+  }
+
+  ASSERT_TRUE(display_service_tester_.GetNotification(kExampleNotificationId));
+
+  // Revoke permission for any origin to display notifications.
+  profile_.SetNotificationPermissionStatus(
+      blink::mojom::PermissionStatus::DENIED);
+
+  // Now simulate a click on the notification. It should be automatically closed
+  // by the PersistentNotificationHandler.
+  {
+    EXPECT_CALL(*mock_logger_,
+                LogPersistentNotificationClickWithoutPermission());
+
+    display_service_tester_.SimulateClick(
+        NotificationHandler::Type::WEB_PERSISTENT, kExampleNotificationId,
+        base::nullopt /* action_index */, base::nullopt /* reply */);
+  }
+
+  EXPECT_FALSE(display_service_tester_.GetNotification(kExampleNotificationId));
+}
+
 TEST_F(PersistentNotificationHandlerTest, OnClose_ByUser) {
   EXPECT_CALL(*mock_logger_, LogPersistentNotificationClosedByUser());
 
@@ -136,5 +187,3 @@
                 .content_setting,
             CONTENT_SETTING_BLOCK);
 }
-
-}  // namespace
diff --git a/chrome/browser/notifications/platform_notification_service_impl.h b/chrome/browser/notifications/platform_notification_service_impl.h
index 4b0fcf7..ab8ed4e0 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.h
+++ b/chrome/browser/notifications/platform_notification_service_impl.h
@@ -81,6 +81,7 @@
 
  private:
   friend struct base::DefaultSingletonTraits<PlatformNotificationServiceImpl>;
+  friend class PersistentNotificationHandlerTest;
   friend class PlatformNotificationServiceBrowserTest;
   friend class PlatformNotificationServiceTest;
   friend class PushMessagingBrowserTest;
@@ -112,6 +113,9 @@
   base::string16 DisplayNameForContextMessage(Profile* profile,
                                               const GURL& origin) const;
 
+  // Clears |closed_notifications_|. Should only be used for testing purposes.
+  void ClearClosedNotificationsForTesting() { closed_notifications_.clear(); }
+
   // Tracks the id of persistent notifications that have been closed
   // programmatically to avoid dispatching close events for them.
   std::unordered_set<std::string> closed_notifications_;
diff --git a/chrome/browser/prefs/pref_service_incognito_whitelist.cc b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
index df76458..a2163097 100644
--- a/chrome/browser/prefs/pref_service_incognito_whitelist.cc
+++ b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
@@ -30,7 +30,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/public/cpp/ash_pref_names.h"
-#include "ui/base/ime/chromeos/extension_ime_util.h"
 #endif  // defined(OS_CHROMEOS)
 
 namespace {
@@ -232,12 +231,10 @@
     prefs::kSessionWaitForInitialUserActivity, prefs::kLastSessionType,
     prefs::kLastSessionLength, prefs::kTermsOfServiceURL,
     prefs::kAttestationEnabled, prefs::kAttestationExtensionWhitelist,
-    prefs::kFirstRunTutorialShown, prefs::kSAMLOfflineSigninTimeLimit,
-    prefs::kSAMLLastGAIASignInTime, prefs::kTimeOnOobe,
+    prefs::kSAMLOfflineSigninTimeLimit, prefs::kSAMLLastGAIASignInTime,
     prefs::kFileSystemProviderMounted, prefs::kTouchVirtualKeyboardEnabled,
     prefs::kWakeOnWifiDarkConnect,
-    prefs::kCaptivePortalAuthenticationIgnoresProxy,
-    prefs::kForceMaximizeOnFirstRun, prefs::kPlatformKeys,
+    prefs::kCaptivePortalAuthenticationIgnoresProxy, prefs::kPlatformKeys,
     prefs::kUnifiedDesktopEnabledByDefault,
     prefs::kHatsLastInteractionTimestamp, prefs::kHatsSurveyCycleEndTimestamp,
     prefs::kHatsDeviceIsSelected, prefs::kQuickUnlockPinSecret,
@@ -303,12 +300,6 @@
     prefs::kEnableReferrers, prefs::kEnableDoNotTrack,
     prefs::kEnableEncryptedMedia,
 
-    prefs::kImportBookmarks, prefs::kImportHistory, prefs::kImportHomepage,
-    prefs::kImportSavedPasswords, prefs::kImportSearchEngine,
-
-    prefs::kImportDialogBookmarks, prefs::kImportDialogHistory,
-    prefs::kImportDialogSavedPasswords, prefs::kImportDialogSearchEngine,
-
     prefs::kInvertNotificationShown,
 
     prefs::kPrintingEnabled, prefs::kPrintPreviewDisabled,
@@ -441,21 +432,15 @@
 
 #if defined(OS_CHROMEOS)
     prefs::kDeviceSettingsCache, prefs::kHardwareKeyboardLayout,
-    prefs::kCarrierDealPromoShown, prefs::kShouldAutoEnroll,
-    prefs::kAutoEnrollmentPowerLimit, prefs::kDeviceActivityTimes,
+    prefs::kCarrierDealPromoShown, prefs::kDeviceActivityTimes,
     prefs::kUserActivityTimes, prefs::kExternalStorageDisabled,
     prefs::kExternalStorageReadOnly, prefs::kOwnerPrimaryMouseButtonRight,
     prefs::kUptimeLimit, prefs::kRebootAfterUpdate,
-    prefs::kDeviceRobotAnyApiRefreshToken, prefs::kDeviceEnrollmentRequisition,
-    prefs::kDeviceEnrollmentAutoStart, prefs::kDeviceEnrollmentCanExit,
-    prefs::kDeviceDMToken, prefs::kTimesHIDDialogShown,
-    prefs::kUsersLastInputMethod, prefs::kEchoCheckedOffers,
-    prefs::kInitialLocale, prefs::kOobeComplete, prefs::kOobeScreenPending,
-    prefs::kOobeControllerDetected, prefs::kCanShowOobeGoodiesPage,
-    prefs::kDeviceRegistered, prefs::kEnrollmentRecoveryRequired,
-    prefs::kServerBackedDeviceState, prefs::kCustomizationDefaultWallpaperURL,
-    prefs::kLogoutStartedLast,
-    // prefs::kConsumerManagementStage,
+    prefs::kDeviceRobotAnyApiRefreshToken, prefs::kDeviceDMToken,
+    prefs::kTimesHIDDialogShown, prefs::kUsersLastInputMethod,
+    prefs::kEchoCheckedOffers, prefs::kCachedMultiProfileUserBehavior,
+    prefs::kInitialLocale, prefs::kServerBackedDeviceState,
+    prefs::kCustomizationDefaultWallpaperURL, prefs::kLogoutStartedLast,
     prefs::kIsBootstrappingSlave, prefs::kNetworkThrottlingEnabled,
     prefs::kPowerMetricsDailySample, prefs::kPowerMetricsIdleScreenDimCount,
     prefs::kPowerMetricsIdleScreenOffCount,
@@ -657,16 +642,6 @@
 
     // components/web_resource/web_resource_pref_names.h
     prefs::kEulaAccepted,
-
-// ui/base/ime/chromeos/extension_ime_util.h
-#if defined(OS_CHROMEOS)
-    chromeos::extension_ime_util::kBrailleImeExtensionId,
-    chromeos::extension_ime_util::kBrailleImeExtensionPath,
-
-    // TODO(https://crbug.com/861722): Check with code owners why this pref is
-    // required in tests, if possible, update tests and remove.
-    chromeos::extension_ime_util::kBrailleImeEngineId,
-#endif  // defined(OS_CHROMEOS)
 };
 
 }  // namespace
diff --git a/chrome/browser/printing/print_preview_message_handler.cc b/chrome/browser/printing/print_preview_message_handler.cc
index 10fe508..8ed18adc 100644
--- a/chrome/browser/printing/print_preview_message_handler.cc
+++ b/chrome/browser/printing/print_preview_message_handler.cc
@@ -141,9 +141,7 @@
 
     // Use utility process to convert skia metafile to pdf.
     client->DoCompositePageToPdf(
-        params.document_cookie, render_frame_host, page_number,
-        content.metafile_data_handle, content.data_size,
-        content.subframe_content_info,
+        params.document_cookie, render_frame_host, page_number, content,
         base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfPageDone,
                        weak_ptr_factory_.GetWeakPtr(), page_number, ids));
   } else {
@@ -175,8 +173,7 @@
     DCHECK(client);
 
     client->DoCompositeDocumentToPdf(
-        params.document_cookie, render_frame_host, content.metafile_data_handle,
-        content.data_size, content.subframe_content_info,
+        params.document_cookie, render_frame_host, content,
         base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfDocumentDone,
                        weak_ptr_factory_.GetWeakPtr(),
                        params.expected_pages_count, ids));
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index 7433af1..ce52a5bc 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -317,8 +317,7 @@
   auto* client = PrintCompositeClient::FromWebContents(web_contents());
   if (IsOopifEnabled() && print_job_->document()->settings().is_modifiable()) {
     client->DoCompositeDocumentToPdf(
-        params.document_cookie, render_frame_host, content.metafile_data_handle,
-        content.data_size, content.subframe_content_info,
+        params.document_cookie, render_frame_host, content,
         base::BindOnce(&PrintViewManagerBase::OnComposePdfDone,
                        weak_ptr_factory_.GetWeakPtr(), params));
     return;
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher.cc b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
index 897a037..b259051 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher.cc
@@ -424,36 +424,45 @@
     web_contents_data->TabWindowActivated();
 }
 
-void TabActivityWatcher::TabInsertedAt(TabStripModel* tab_strip_model,
-                                       content::WebContents* contents,
-                                       int index,
-                                       bool foreground) {
-  // Ensure the WebContentsData is created to observe this WebContents since it
-  // may represent a newly created tab.
-  WebContentsData::CreateForWebContents(contents);
-  WebContentsData::FromWebContents(contents)->TabInserted(foreground);
-}
+void TabActivityWatcher::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  switch (change.type()) {
+    case TabStripModelChange::kInserted: {
+      for (const auto& delta : change.deltas()) {
+        // Ensure the WebContentsData is created to observe this WebContents
+        // since it may represent a newly created tab.
+        WebContentsData::CreateForWebContents(delta.insert.contents);
+        WebContentsData::FromWebContents(delta.insert.contents)
+            ->TabInserted(selection.new_contents == delta.insert.contents);
+      }
+      break;
+    }
+    case TabStripModelChange::kRemoved: {
+      for (const auto& delta : change.deltas())
+        WebContentsData::FromWebContents(delta.remove.contents)->TabDetached();
+      break;
+    }
+    case TabStripModelChange::kReplaced: {
+      for (const auto& delta : change.deltas()) {
+        WebContentsData* old_web_contents_data =
+            WebContentsData::FromWebContents(delta.replace.old_contents);
+        old_web_contents_data->WasReplaced();
 
-void TabActivityWatcher::TabDetachedAt(content::WebContents* contents,
-                                       int index,
-                                       bool was_active) {
-  WebContentsData::FromWebContents(contents)->TabDetached();
-}
+        // Ensure the WebContentsData is created to observe this WebContents
+        // since it likely hasn't been inserted into a tabstrip before.
+        WebContentsData::CreateForWebContents(delta.replace.new_contents);
 
-void TabActivityWatcher::TabReplacedAt(TabStripModel* tab_strip_model,
-                                       content::WebContents* old_contents,
-                                       content::WebContents* new_contents,
-                                       int index) {
-  WebContentsData* old_web_contents_data =
-      WebContentsData::FromWebContents(old_contents);
-  old_web_contents_data->WasReplaced();
-
-  // Ensure the WebContentsData is created to observe this WebContents since it
-  // likely hasn't been inserted into a tabstrip before.
-  WebContentsData::CreateForWebContents(new_contents);
-
-  WebContentsData::FromWebContents(new_contents)
-      ->DidReplace(*old_web_contents_data);
+        WebContentsData::FromWebContents(delta.replace.new_contents)
+            ->DidReplace(*old_web_contents_data);
+      }
+      break;
+    }
+    case TabStripModelChange::kMoved:
+    case TabStripModelChange::kSelectionOnly:
+      break;
+  }
 }
 
 void TabActivityWatcher::TabPinnedStateChanged(TabStripModel* tab_strip_model,
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher.h b/chrome/browser/resource_coordinator/tab_activity_watcher.h
index 7c5efc7e..144f0f9 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher.h
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher.h
@@ -54,17 +54,10 @@
   void OnBrowserSetLastActive(Browser* browser) override;
 
   // TabStripModelObserver:
-  void TabInsertedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* contents,
-                     int index,
-                     bool foreground) override;
-  void TabDetachedAt(content::WebContents* contents,
-                     int index,
-                     bool was_active) override;
-  void TabReplacedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* old_contents,
-                     content::WebContents* new_contents,
-                     int index) override;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
   void TabPinnedStateChanged(TabStripModel* tab_strip_model,
                              content::WebContents* contents,
                              int index) override;
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index 7d87b13..4a172ca 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -154,8 +154,15 @@
   TabLifecycleUnit* focused_lifecycle_unit =
       focused_web_contents ? GetTabLifecycleUnit(focused_web_contents)
                            : nullptr;
-  DCHECK(!focused_web_contents || focused_lifecycle_unit);
-  UpdateFocusedTabTo(focused_lifecycle_unit);
+
+  // TODO(sangwoo.ko) We are refactoring TabStripModel API and this is
+  // workaround to avoid DCHECK failure on Chromium os. This DCHECK is supposing
+  // that OnTabInserted() is always called before OnBrowserSetLastActive is
+  // called but it's not. After replacing old API use in BrowserView,
+  // restore this to DCHECK(!focused_web_contents || focused_lifecycle_unit);
+  // else case will be handled by following OnTabInserted().
+  if (!focused_web_contents || focused_lifecycle_unit)
+    UpdateFocusedTabTo(focused_lifecycle_unit);
 }
 
 void TabLifecycleUnitSource::UpdateFocusedTabTo(
@@ -169,9 +176,8 @@
   focused_lifecycle_unit_ = new_focused_lifecycle_unit;
 }
 
-void TabLifecycleUnitSource::TabInsertedAt(TabStripModel* tab_strip_model,
+void TabLifecycleUnitSource::OnTabInserted(TabStripModel* tab_strip_model,
                                            content::WebContents* contents,
-                                           int index,
                                            bool foreground) {
   TabLifecycleUnit* lifecycle_unit = GetTabLifecycleUnit(contents);
   if (lifecycle_unit) {
@@ -197,9 +203,7 @@
   }
 }
 
-void TabLifecycleUnitSource::TabDetachedAt(content::WebContents* contents,
-                                           int index,
-                                           bool was_active) {
+void TabLifecycleUnitSource::OnTabDetached(content::WebContents* contents) {
   TabLifecycleUnit* lifecycle_unit = GetTabLifecycleUnit(contents);
   DCHECK(lifecycle_unit);
   if (focused_lifecycle_unit_ == lifecycle_unit)
@@ -207,18 +211,8 @@
   lifecycle_unit->SetTabStripModel(nullptr);
 }
 
-void TabLifecycleUnitSource::ActiveTabChanged(
-    content::WebContents* old_contents,
-    content::WebContents* new_contents,
-    int index,
-    int reason) {
-  UpdateFocusedTab();
-}
-
-void TabLifecycleUnitSource::TabReplacedAt(TabStripModel* tab_strip_model,
-                                           content::WebContents* old_contents,
-                                           content::WebContents* new_contents,
-                                           int index) {
+void TabLifecycleUnitSource::OnTabReplaced(content::WebContents* old_contents,
+                                           content::WebContents* new_contents) {
   auto* old_contents_holder =
       TabLifecycleUnitHolder::FromWebContents(old_contents);
   DCHECK(old_contents_holder);
@@ -233,6 +227,37 @@
   new_contents_holder->lifecycle_unit()->SetWebContents(new_contents);
 }
 
+void TabLifecycleUnitSource::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  switch (change.type()) {
+    case TabStripModelChange::kInserted: {
+      for (const auto& delta : change.deltas()) {
+        OnTabInserted(tab_strip_model, delta.insert.contents,
+                      selection.new_contents == delta.insert.contents);
+      }
+      break;
+    }
+    case TabStripModelChange::kRemoved: {
+      for (const auto& delta : change.deltas())
+        OnTabDetached(delta.remove.contents);
+      break;
+    }
+    case TabStripModelChange::kReplaced: {
+      for (const auto& delta : change.deltas())
+        OnTabReplaced(delta.replace.old_contents, delta.replace.new_contents);
+      break;
+    }
+    case TabStripModelChange::kMoved:
+    case TabStripModelChange::kSelectionOnly:
+      break;
+  }
+
+  if (selection.active_tab_changed() && !tab_strip_model->empty())
+    UpdateFocusedTab();
+}
+
 void TabLifecycleUnitSource::TabChangedAt(content::WebContents* contents,
                                           int index,
                                           TabChangeType change_type) {
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index ed089f35..226708f 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -111,22 +111,20 @@
   // TabInsertedAt() is called.
   void UpdateFocusedTabTo(TabLifecycleUnit* new_focused_lifecycle_unit);
 
-  // TabStripModelObserver:
-  void TabInsertedAt(TabStripModel* tab_strip_model,
+  // Methods called by OnTabStripModelChanged()
+  void OnTabInserted(TabStripModel* tab_strip_model,
                      content::WebContents* contents,
-                     int index,
-                     bool foreground) override;
-  void TabDetachedAt(content::WebContents* contents,
-                     int index,
-                     bool was_active) override;
-  void ActiveTabChanged(content::WebContents* old_contents,
-                        content::WebContents* new_contents,
-                        int index,
-                        int reason) override;
-  void TabReplacedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* old_contents,
-                     content::WebContents* new_contents,
-                     int index) override;
+                     bool foreground);
+  void OnTabDetached(content::WebContents* contents);
+  void OnTabReplaced(content::WebContents* old_contents,
+                     content::WebContents* new_contents);
+
+  // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
+
   void TabChangedAt(content::WebContents* contents,
                     int index,
                     TabChangeType change_type) override;
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index b379760a..b9139178 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -546,10 +546,8 @@
   // calling PurgeBrowserMemory() before CRITICAL is reached.
 }
 
-void TabManager::ActiveTabChanged(content::WebContents* old_contents,
-                                  content::WebContents* new_contents,
-                                  int index,
-                                  int reason) {
+void TabManager::OnActiveTabChanged(content::WebContents* old_contents,
+                                    content::WebContents* new_contents) {
   // An active tab is not purged.
   // Calling GetWebContentsData() early ensures that the WebContentsData is
   // created for |new_contents|, which |stats_collector_| expects.
@@ -571,12 +569,10 @@
   ResumeTabNavigationIfNeeded(new_contents);
 }
 
-void TabManager::TabInsertedAt(TabStripModel* tab_strip_model,
-                               content::WebContents* contents,
-                               int index,
+void TabManager::OnTabInserted(content::WebContents* contents,
                                bool foreground) {
   // Only interested in background tabs, as foreground tabs get taken care of by
-  // ActiveTabChanged.
+  // OnActiveTabChanged.
   if (foreground)
     return;
 
@@ -588,11 +584,24 @@
       GetTimeToPurge(min_time_to_purge_, max_time_to_purge_));
 }
 
-void TabManager::TabReplacedAt(TabStripModel* tab_strip_model,
-                               content::WebContents* old_contents,
-                               content::WebContents* new_contents,
-                               int index) {
-  WebContentsData::CopyState(old_contents, new_contents);
+void TabManager::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  if (change.type() == TabStripModelChange::kInserted) {
+    for (const auto& delta : change.deltas()) {
+      OnTabInserted(delta.insert.contents,
+                    delta.insert.contents == selection.new_contents);
+    }
+  } else if (change.type() == TabStripModelChange::kReplaced) {
+    for (const auto& delta : change.deltas()) {
+      WebContentsData::CopyState(delta.replace.old_contents,
+                                 delta.replace.new_contents);
+    }
+  }
+
+  if (selection.active_tab_changed() && !tab_strip_model->empty())
+    OnActiveTabChanged(selection.old_contents, selection.new_contents);
 }
 
 void TabManager::OnStartTracking(content::WebContents* web_contents,
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index da4d69e..9759d423 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -311,19 +311,16 @@
   void OnMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
+  // Methods called by OnTabStripModelChanged()
+  void OnActiveTabChanged(content::WebContents* old_contents,
+                          content::WebContents* new_contents);
+  void OnTabInserted(content::WebContents* contents, bool foreground);
+
   // TabStripModelObserver:
-  void ActiveTabChanged(content::WebContents* old_contents,
-                        content::WebContents* new_contents,
-                        int index,
-                        int reason) override;
-  void TabInsertedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* contents,
-                     int index,
-                     bool foreground) override;
-  void TabReplacedAt(TabStripModel* tab_strip_model,
-                     content::WebContents* old_contents,
-                     content::WebContents* new_contents,
-                     int index) override;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
 
   // TabLoadTracker::Observer:
   void OnStartTracking(content::WebContents* web_contents,
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index a929945..415bc01 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -632,9 +632,7 @@
   EXPECT_TRUE(tab_manager_->IsNavigationDelayedForTest(nav_handle3_.get()));
 
   // Simulate selecting tab 3, which should start loading immediately.
-  tab_manager_->ActiveTabChanged(
-      contents1_.get(), contents3_.get(), 2,
-      TabStripModelObserver::CHANGE_REASON_USER_GESTURE);
+  tab_manager_->OnActiveTabChanged(contents1_.get(), contents3_.get());
 
   EXPECT_TRUE(tab_manager_->IsTabLoadingForTest(contents1_.get()));
   EXPECT_TRUE(tab_manager_->IsTabLoadingForTest(contents3_.get()));
diff --git a/chrome/browser/resources/chromeos/login/active_directory_password_change.html b/chrome/browser/resources/chromeos/login/active_directory_password_change.html
index dd62091..e3b30c2 100644
--- a/chrome/browser/resources/chromeos/login/active_directory_password_change.html
+++ b/chrome/browser/resources/chromeos/login/active_directory_password_change.html
@@ -46,17 +46,27 @@
             <gaia-input-form id="inputForm" on-submit="onSubmit_"
                 i18n-values="button-text:offlineLoginNextBtn">
               <gaia-input slot="inputs" id="oldPassword" type="password"
-                  i18n-values="error:adPassChangeOldPasswordError;
-                               label:adPassChangeOldPasswordHint" required>
+                  required>
+                <div slot="label" i18n-content="adPassChangeOldPasswordHint">
+                </div>
+                <div slot="error" i18n-content="adPassChangeOldPasswordError">
+                </div>
               </gaia-input>
               <gaia-input slot="inputs" id="newPassword1" type="password"
-                  i18n-values="error:adPassChangeNewPasswordRejected;
-                               label:adPassChangeNewPasswordHint" required>
+                  required>
+                <div slot="label" i18n-content="adPassChangeNewPasswordHint">
+                </div>
+                <div slot="error"
+                    i18n-content="adPassChangeNewPasswordRejected">
+                </div>
               </gaia-input>
               <gaia-input slot="inputs" id="newPassword2" type="password"
-                  i18n-values="error:adPassChangePasswordsMismatch;
-                               label:adPassChangeRepeatNewPasswordHint"
                   required>
+                <div slot="label"
+                    i18n-content="adPassChangeRepeatNewPasswordHint">
+                </div>
+                <div slot="error" i18n-content="adPassChangePasswordsMismatch">
+                </div>
               </gaia-input>
             </gaia-input-form>
           </div>
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.css b/chrome/browser/resources/chromeos/login/gaia_input.css
index 58a371b..1316b72 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input.css
+++ b/chrome/browser/resources/chromeos/login/gaia_input.css
@@ -22,7 +22,7 @@
   padding: 0;
 }
 
-paper-input-container > label > span {
+paper-input-container > label > ::slotted(*) {
   color: rgba(0, 0, 0, 0.54);
 }
 
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.html b/chrome/browser/resources/chromeos/login/gaia_input.html
index 460c8a9b..0d09d9a 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input.html
+++ b/chrome/browser/resources/chromeos/login/gaia_input.html
@@ -17,16 +17,18 @@
     <gaia-input value="Simple text input" required></gaia-input>
     <gaia-input type="email" domain="example.com"></gaia-input>
 
-  Attributes:
+  Slots:
+    'error' - error message displayed in case if 'isInvalid' is true.
     'label' - same as <paper-input>'s 'label'.
+
+  Attributes:
     'value' - same as <input>'s 'value'.
     'type' - same as <input>'s 'type'.
     'domain' - optional attribute for email input. The domain is displayed in
                the end of input field, if user is not provided any.
-    'error' - error message displayed in case if 'isInvalid' is true.
-    'isInvalid' - whether input data is invalid. Note: it is not changed
-                  automatically. Can be changed manually or with checkValidity()
-                  method.
+    'isInvalid' - whether input data is invalid. Supports two-way binding.
+	          Note: it is not changed automatically. Can be changed manually
+                  or with checkValidity() method.
     'required' - whether empty field is invalid.
     'pattern' - regular expression. If set input would be validated against it
                 in checkValidity function. Ignored if 'type' == 'email'.
@@ -35,6 +37,8 @@
     'focus'         - focuses input field.
     'checkValidity' - returns current validity state of the input form. Updates
                       'isInvalid' at the end.
+  Events:
+    'invalid-state-cleared' - Fired when isInvalid changes from true to false.
 -->
 <dom-module id="gaia-input">
   <link rel="stylesheet" href="gaia_input.css">
@@ -42,19 +46,21 @@
 
   <template>
     <paper-input-container id="decorator" on-tap="onTap"
-        invalid="[[isInvalid]]" disabled$="[[disabled]]">
-      <label slot="label"><span>[[label]]</span></label>
+        invalid="{{isInvalid}}" disabled$="[[disabled]]">
+      <label slot="label">
+        <slot name="label"></slot>
+      </label>
       <iron-input id="ironInput" slot="input" class="flex"
           bind-value="{{value}}">
         <input id="input" on-keydown="onKeyDown"
-          value="{{value::input}}" invalid="[[isInvalid]]"
+          value="{{value::input}}" invalid="{{isInvalid}}"
           required$="[[required]]" disabled$="[[disabled]]"
-          pattern$="[[pattern]]">
+          pattern$="[[pattern]]" aria-label$="[[label]]">
       </iron-input>
       <span id="domainLabel" slot="suffix">[[domain]]</span>
-      <template is="dom-if" if="[[error]]">
-        <paper-input-error slot="add-on">[[error]]</paper-input-error>
-      </template>
+      <paper-input-error slot="add-on" hidden="[[!isInvalid]]">
+        <slot name="error"></slot>
+      </paper-input-error>
     </paper-input-container>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.js b/chrome/browser/resources/chromeos/login/gaia_input.js
index a273b57..90549526 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input.js
+++ b/chrome/browser/resources/chromeos/login/gaia_input.js
@@ -9,7 +9,6 @@
     is: 'gaia-input',
 
     properties: {
-      label: String,
       value: {notify: true, observer: 'updateDomainVisibility_', type: String},
 
       type: {observer: 'typeChanged_', type: String},
@@ -20,9 +19,7 @@
 
       required: Boolean,
 
-      error: String,
-
-      isInvalid: Boolean,
+      isInvalid: {type: Boolean, notify: true},
 
       pattern: String
     },
@@ -67,6 +64,6 @@
         }
       }
       this.updateDomainVisibility_();
-    }
+    },
   };
 })());
diff --git a/chrome/browser/resources/chromeos/login/gaia_input_form.html b/chrome/browser/resources/chromeos/login/gaia_input_form.html
index 29cfbb9e..df5a5e1 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input_form.html
+++ b/chrome/browser/resources/chromeos/login/gaia_input_form.html
@@ -31,7 +31,8 @@
     <div on-keydown="onKeyDown_">
       <slot id="inputs" name="inputs"></slot>
       <div class="horizontal-reverse justified layout center">
-        <gaia-button id="button" on-tap="onButtonClicked_" class="self-end">
+        <gaia-button id="button" on-tap="onButtonClicked_" class="self-end"
+            hidden="[[!buttonText]]">
           <span>[[buttonText]]</span>
         </gaia-button>
         <slot></slot>
diff --git a/chrome/browser/resources/chromeos/login/gaia_input_form.js b/chrome/browser/resources/chromeos/login/gaia_input_form.js
index 777b631..08b52ac 100644
--- a/chrome/browser/resources/chromeos/login/gaia_input_form.js
+++ b/chrome/browser/resources/chromeos/login/gaia_input_form.js
@@ -11,7 +11,10 @@
       observer: 'onDisabledChanged_',
     },
 
-    buttonText: String
+    buttonText: {
+      type: String,
+      value: '',
+    }
   },
 
   /** @public */
diff --git a/chrome/browser/resources/chromeos/login/gaia_password_changed.html b/chrome/browser/resources/chromeos/login/gaia_password_changed.html
index e52e693..30877f3f 100644
--- a/chrome/browser/resources/chromeos/login/gaia_password_changed.html
+++ b/chrome/browser/resources/chromeos/login/gaia_password_changed.html
@@ -62,8 +62,9 @@
               disabled="[[disabled]]" on-submit="onPasswordSubmitted_"
               i18n-values="button-text:nextButtonText">
             <gaia-input slot="inputs" id="oldPasswordInput" type="password"
-                i18n-values="error:oldPasswordIncorrect;
-                             label:oldPasswordHint" required>
+                required>
+              <div slot="label" i18n-content="oldPasswordHint"></div>
+              <div slot="error" i18n-content="oldPasswordIncorrect"></div>
             </gaia-input>
             <gaia-button type="link" on-tap="onForgotPasswordClicked_"
                 i18n-content="forgotOldPasswordButtonText">
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.html b/chrome/browser/resources/chromeos/login/offline_ad_login.html
index b6f5f38..586fdeb 100644
--- a/chrome/browser/resources/chromeos/login/offline_ad_login.html
+++ b/chrome/browser/resources/chromeos/login/offline_ad_login.html
@@ -62,9 +62,9 @@
             i18n-values="button-text:adUnlockButton"
             hidden="[[!unlockPasswordStep]]">
           <gaia-input id="unlockPasswordInput" type="password" slot="inputs"
-              i18n-values="error:adUnlockIncorrectPassword;
-                           label:adUnlockPassword"
               required>
+            <div slot="label" i18n-content="adUnlockPassword"></div>
+            <div slot="error" i18n-content="adUnlockIncorrectPassword"></div>
           </gaia-input>
           <gaia-button id="skipButton" on-tap="onSkipClicked_">
             $i18n{adUnlockPasswordSkip}
@@ -85,16 +85,19 @@
             i18n-values="button-text:offlineLoginNextBtn"
             hidden="[[unlockPasswordStep]]">
           <gaia-input id="machineNameInput" required slot="inputs"
-              hidden="[[!isDomainJoin]]" error="[[machineNameError]]"
-              i18n-values="label:oauthEnrollAdMachineNameInput">
+              hidden="[[!isDomainJoin]]">
+            <div slot="label" i18n-content="oauthEnrollAdMachineNameInput">
+            </div>
+            <div slot="error">[[machineNameError]]</div>
           </gaia-input>
           <gaia-input slot="inputs" id="userInput" type="email" required
-              domain="[[userRealm]]" label="[[userNameLabel]]"
-              i18n-values="error:adLoginInvalidUsername">
+              domain="[[userRealm]]">
+            <div slot="label">[[userNameLabel]]</div>
+            <div slot="error" i18n-content="adLoginInvalidUsername"></div>
           </gaia-input>
-          <gaia-input slot="inputs" id="passwordInput" type="password" required
-              i18n-values="error:adLoginInvalidPassword;
-                           label:adLoginPassword">
+          <gaia-input slot="inputs" id="passwordInput" type="password" required>
+            <div slot="label" i18n-content="adLoginPassword"></div>
+            <div slot="error" i18n-content="adLoginInvalidPassword"></div>
           </gaia-input>
           <gaia-button id="backToUnlockButton" on-tap="onBackToUnlock_"
               type="link" hidden>
@@ -113,7 +116,8 @@
         $i18n{adJoinMoreOptions}
       </div>
       <div slot="body">
-        <gaia-input id="orgUnitInput" label="$i18n{adJoinOrgUnit}" required>
+        <gaia-input id="orgUnitInput" required>
+          <div slot="label" i18n-content="adJoinOrgUnit"></div>
         </gaia-input>
       </div>
       <div slot="body">
diff --git a/chrome/browser/resources/chromeos/login/offline_gaia.html b/chrome/browser/resources/chromeos/login/offline_gaia.html
index 2395e35..79ba714 100644
--- a/chrome/browser/resources/chromeos/login/offline_gaia.html
+++ b/chrome/browser/resources/chromeos/login/offline_gaia.html
@@ -76,9 +76,13 @@
                 disabled="[[disabled]]"
                 button-text="[[i18nDynamic(locale, 'offlineLoginNextBtn')]]">
               <gaia-input slot="inputs" id="emailInput" type="email" required
-                  domain="[[emailDomain]]"
-                  error="[[i18nDynamic(locale, 'offlineLoginInvalidEmail')]]"
-                  label="[[i18nDynamic(locale, 'offlineLoginEmail')]]">
+                  domain="[[emailDomain]]">
+                <div slot="label">
+                  [[i18nDynamic(locale, 'offlineLoginEmail')]]
+                </div>
+                <div slot="error">
+                  [[i18nDynamic(locale, 'offlineLoginInvalidEmail')]]
+                </div>
               </gaia-input>
             </gaia-input-form>
           </div>
@@ -92,10 +96,13 @@
                 on-submit="onPasswordSubmitted_"
                 button-text="[[i18nDynamic(locale, 'offlineLoginNextBtn')]]">
               <gaia-input slot="inputs" id="passwordInput" type="password"
-                  error="[[i18nDynamic(locale,
-                                       'offlineLoginInvalidPassword')]]"
-                  label="[[i18nDynamic(locale, 'offlineLoginPassword')]]"
                   required>
+                <div slot="label">
+                  [[i18nDynamic(locale, 'offlineLoginPassword')]]
+                </div>
+                <div slot="error">
+                  [[i18nDynamic(locale, 'offlineLoginInvalidPassword')]]
+                </div>
               </gaia-input>
               <gaia-button type="link" on-tap="onForgotPasswordClicked_">
                 [[i18nDynamic(locale, 'offlineLoginForgotPasswordBtn')]]
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
index fe8deed..90f66594 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
@@ -119,11 +119,13 @@
                         i18n-content="oauthEnrollExplainAttributeLink"></a>
                 </div>
                 <gaia-input id="oauth-enroll-asset-id" type="text"
-                    class="autofocus"
-                    i18n-values="label:oauthEnrollAssetIdLabel">
+                    class="autofocus">
+                  <div slot="label" i18n-content="oauthEnrollAssetIdLabel">
+                  </div>
                 </gaia-input>
-                <gaia-input id="oauth-enroll-location" type="text"
-                    i18n-values="label:oauthEnrollLocationLabel">
+                <gaia-input id="oauth-enroll-location" type="text">
+                  <div slot="label" i18n-content="oauthEnrollLocationLabel">
+                  </div>
                 </gaia-input>
             </div>
             <div slot="footer" class="footer">
diff --git a/chrome/browser/resources/chromeos/login/saml_confirm_password.html b/chrome/browser/resources/chromeos/login/saml_confirm_password.html
index 3dfdf30..54207c15 100644
--- a/chrome/browser/resources/chromeos/login/saml_confirm_password.html
+++ b/chrome/browser/resources/chromeos/login/saml_confirm_password.html
@@ -64,9 +64,9 @@
             </gaia-input>
             <template is="dom-if" if="[[manualInput]]">
               <gaia-input slot="inputs" id="confirmPasswordInput"
-                  type="password" required
-                  label$="{{getConfirmPasswordInputLabel_()}}"
-                  error$="{{getConfirmPasswordInputError_()}}">
+                  type="password" required>
+                <div slot="label">[[getConfirmPasswordInputLabel_()]]</div>
+                <div slot="error">[[getConfirmPasswordInputError_()]]</div>
               </gaia-input>
             </template>
           </gaia-input-form>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/icons.html b/chrome/browser/resources/chromeos/multidevice_setup/icons.html
index d704a2c..0232cf47 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/icons.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/icons.html
@@ -2,7 +2,7 @@
 
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 
-<iron-iconset-svg name="multidevice-setup-icons" size="32">
+<iron-iconset-svg name="multidevice-setup-icons-32" size="32">
   <svg>
     <defs>
       <g id="google-g" fill="none" fill-rule="evenodd">
@@ -19,3 +19,18 @@
     </defs>
   </svg>
 </iron-iconset-svg>
+<iron-iconset-svg name="multidevice-setup-icons-20" size="20">
+  <svg>
+    <defs>
+      <g id="messages" fill="none" fill-rule="evenodd">
+        <path d="M16.3107503,3 L3.66666667,3 C2.75,3 2,3.75 2,4.66666667 L2,18.3161621 L5.33333333,15 L16.3107503,15 C17.227417,15 17.977417,14.2328288 17.977417,13.3161621 L17.977417,4.66666667 C17.977417,3.75 17.227417,3 16.3107503,3 Z M16,13 L4,13 L4,5 L16,5 L16,13 Z M6,8 L8,8 L8,10 L6,10 L6,8 Z M9,8 L11,8 L11,10 L9,10 L9,8 Z M12,8 L14,8 L14,10 L12,10 L12,8 Z" fill="#9AA0A6"></path>
+      </g>
+      <g id="downloads" fill="none" fill-rule="evenodd">
+        <path d="M2,13 L4,13 L4,16 L16,16 L16,13 L18,13 L18,16 C18,17.1 17.1,18 16,18 L4,18 C2.9,18 2,17.1 2,16 L2,13 Z M13.59,7.59 L11,10.17 L11,2 L9,2 L9,10.17 L6.41,7.59 L5,9 L10,14 L15,9 L13.59,7.59 Z" fill="#9AA0A6"></path>
+      </g>
+      <g id="features" fill="none" fill-rule="evenodd">
+        <path d="M5,5 L18,5 L18,3.5 L5.16080729,3.5 C4.24414063,3.5 3.49414062,4.23125 3.49414062,5.125 L3.49414062,14 L1,14 L1,17 L11,17 L11,14 L5,14 L5,5 Z M18.1666667,6.49829102 L13.3713582,6.49829102 C12.9130249,6.49829102 12.5,6.86391602 12.5,7.31079102 L12.5,16.171875 C12.5,16.61875 12.9130249,17 13.3713582,17 L18.1666667,17 C18.625,17 19,16.61875 19,16.171875 L19,7.31079102 C19,6.86391602 18.625,6.49829102 18.1666667,6.49829102 Z M17.5,14 L14,14 L14,8.5 L17.5,8.5 L17.5,14 Z" fill="#9AA0A6"></path>
+      </g>
+    </defs>
+  </svg>
+</iron-iconset-svg>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd
index 479dfd2..2e0ea58 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd
+++ b/chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd
@@ -11,6 +11,12 @@
     <output filename="multidevice_setup_resources.pak" type="data_package" />
   </outputs>
   <release seq="1">
+    <includes>
+      <include name="IDR_MULTIDEVICE_SETUP_START_SETUP_ICON_1X_PNG" file="start_setup_icon_1x.png" type="BINDATA" />
+      <include name="IDR_MULTIDEVICE_SETUP_START_SETUP_ICON_2X_PNG" file="start_setup_icon_2x.png" type="BINDATA" />
+      <include name="IDR_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_ICON_1X_PNG" file="setup_succeeded_icon_1x.png" type="BINDATA" />
+      <include name="IDR_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_ICON_2X_PNG" file="setup_succeeded_icon_2x.png" type="BINDATA" />
+    </includes>
     <structures>
       <structure name="IDR_MULTIDEVICE_SETUP_BUTTON_BAR_HTML"
                  file="button_bar.html"
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_icon_1x.png b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_icon_1x.png
new file mode 100644
index 0000000..03074bd5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_icon_1x.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_icon_2x.png b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_icon_2x.png
new file mode 100644
index 0000000..271b048
--- /dev/null
+++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_icon_2x.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html
index 9b28346..7960dd2 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/setup_succeeded_page.html
@@ -8,13 +8,15 @@
 <dom-module id="setup-succeeded-page">
   <template>
     <style include="iron-flex">
-      #animation-container {
+      #page-icon-container {
         @apply(--layout-horizontal);
         @apply(--layout-center-justified);
       }
 
-      #animation-placeholder {
-        background-color: lightblue;
+      #page-icon {
+        background-image: -webkit-image-set(
+                              url(setup_succeeded_icon_1x.png) 1x,
+                              url(setup_succeeded_icon_2x.png) 2x);
         height: 156px;
         margin-top: 20px;
         width: 416px;
@@ -22,8 +24,8 @@
     </style>
     <ui-page header-text="[[headerText]]" icon-name="google-g">
       <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span>
-      <div id="animation-container" slot="additional-content">
-        <div id="animation-placeholder">Animation Placeholder</div>
+      <div id="page-icon-container" slot="additional-content">
+        <div id="page-icon"></div>
       </div>
     </ui-page>
   </template>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_icon_1x.png b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_icon_1x.png
new file mode 100644
index 0000000..b6a3a19
--- /dev/null
+++ b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_icon_1x.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_icon_2x.png b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_icon_2x.png
new file mode 100644
index 0000000..c7234e8f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_icon_2x.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html
index ed9b325..adf27507 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html
+++ b/chrome/browser/resources/chromeos/multidevice_setup/start_setup_page.html
@@ -2,6 +2,8 @@
 
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="icons.html">
 <link rel="import" href="multidevice_setup_shared_css.html">
 <link rel="import" href="ui_page.html">
 <link rel="import" href="ui_page_container_behavior.html">
@@ -26,16 +28,17 @@
         width: 240px;
       }
 
-      #animation-container {
+      #page-icon-container {
         @apply(--layout-horizontal);
         @apply(--layout-center-justified);
       }
 
-      #animation-placeholder {
-        background-color: lightblue;
-        height: 100px;
+      #page-icon {
+        background-image: -webkit-image-set(url(start_setup_icon_1x.png) 1x,
+                                            url(start_setup_icon_2x.png) 2x);
+        height: 116px;
         margin-top: 20px;
-        width: 300px;
+        width: 320px;
       }
 
       #feature-details-container {
@@ -57,10 +60,11 @@
       .feature-detail iron-icon {
         --iron-icon-height: 20px;
         --iron-icon-width: 20px;
+        position: fixed;
       }
 
       .feature-detail span {
-        padding-left: 8px;
+        padding-left: 28px;
       }
     </style>
     <ui-page header-text="[[headerText]]" icon-name="google-g">
@@ -83,8 +87,8 @@
                 </template>
               </select>
             </div>
-            <div id="animation-container">
-              <div id="animation-placeholder">Animation Placeholder</div>
+            <div id="page-icon-container">
+              <div id="page-icon"></div>
             </div>
           </div>
           <div id="feature-details-container" class="flex">
@@ -92,18 +96,16 @@
               $i18n{startSetupPageFeatureListHeader}
             </div>
             <div class="feature-detail">
-              <!-- TODO(jordynass): Use correct icon. -->
-              <iron-icon icon="multidevice-setup-icons:google-g"></iron-icon>
+              <iron-icon icon="multidevice-setup-icons-20:messages"></iron-icon>
               <span>$i18n{startSetupPageFeatureListAwm}</span>
             </div>
             <div class="feature-detail">
-              <!-- TODO(jordynass): Use correct icon. -->
-              <iron-icon icon="multidevice-setup-icons:google-g"></iron-icon>
+              <iron-icon icon="multidevice-setup-icons-20:downloads">
+              </iron-icon>
               <span>$i18n{startSetupPageFeatureListInstallApps}</span>
             </div>
             <div class="feature-detail">
-              <!-- TODO(jordynass): Use correct icon. -->
-              <iron-icon icon="multidevice-setup-icons:google-g"></iron-icon>
+              <iron-icon icon="multidevice-setup-icons-20:features"></iron-icon>
               <span>$i18n{startSetupPageFeatureListAddFeatures}</span>
             </div>
           </div>
diff --git a/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js b/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js
index f4527d4..d29f7722 100644
--- a/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js
+++ b/chrome/browser/resources/chromeos/multidevice_setup/ui_page.js
@@ -29,6 +29,6 @@
    * @private
    */
   computeIconIdentifier_: function() {
-    return 'multidevice-setup-icons:' + this.iconName;
+    return 'multidevice-setup-icons-32:' + this.iconName;
   },
 });
diff --git a/chrome/browser/resources/discards/discards.js b/chrome/browser/resources/discards/discards.js
index f31731f..66f58cd 100644
--- a/chrome/browser/resources/discards/discards.js
+++ b/chrome/browser/resources/discards/discards.js
@@ -251,7 +251,6 @@
    * @param {string} A string representation of the discarding reason.
    */
   function discardReasonToString(reason) {
-    return 'none';
     switch (reason) {
       case mojom.LifecycleUnitDiscardReason.EXTERNAL:
         return 'external';
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 04c9623..0900b611 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -353,6 +353,7 @@
     window.setTimeout(function() {
       $('custom-bg').style.backgroundImage = '';
     }, 1000);
+    customBackgrounds.clearAttribution();
   }
 
   $(customBackgrounds.IDS.RESTORE_DEFAULT)
diff --git a/chrome/browser/resources/settings/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/multidevice_page/BUILD.gn
index 9a4d478e..46e84a10 100644
--- a/chrome/browser/resources/settings/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/multidevice_page/BUILD.gn
@@ -60,6 +60,7 @@
     ":multidevice_browser_proxy",
     ":multidevice_constants",
     ":multidevice_feature_behavior",
+    "../controls:password_prompt_dialog",
     "//ui/webui/resources/js:cr",
   ]
 }
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js b/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js
index 7caafb7..e57d0861 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_browser_proxy.js
@@ -14,12 +14,12 @@
      * @param {!settings.MultiDeviceFeature} feature The feature whose state
      *     should be set.
      * @param {boolean} enabled Whether the feature should be turned off or on.
-     * @param {String} authToken Proof that the user is authenticated. Needed
-     *     to enable Smart Lock, and Better Together Suite if the Smart Lock
-     *     user pref is enabled.
+     * @param {string=} opt_authToken Proof that the user is authenticated.
+     *     Needed to enable Smart Lock, and Better Together Suite if the Smart
+     *     Lock user pref is enabled.
      * @return {!Promise<boolean>} Whether the operation was successful.
      */
-    setFeatureEnabledState(feature, enabled, authToken) {}
+    setFeatureEnabledState(feature, enabled, opt_authToken) {}
 
     retryPendingHostSetup() {}
   }
@@ -39,9 +39,9 @@
     }
 
     /** @override */
-    setFeatureEnabledState(feature, enabled, authToken) {
+    setFeatureEnabledState(feature, enabled, opt_authToken) {
       return cr.sendWithPromise(
-          'setFeatureEnabledState', feature, enabled, authToken);
+          'setFeatureEnabledState', feature, enabled, opt_authToken);
     }
 
     /** @override */
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html b/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html
index 8e21532d..b52a7be4 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
+<link rel="import" href="multidevice_browser_proxy.html">
 <link rel="import" href="multidevice_constants.html">
 <link rel="import" href="multidevice_feature_behavior.html">
 
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js b/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js
index abd9845..83350e8 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_feature_toggle.js
@@ -66,9 +66,11 @@
    */
   onChange_: function() {
     this.resetChecked_();
-    // TODO(hansberry): Do a password check when this.feature is SMART_LOCK or
-    // BETTER_TOGETHER_SUITE.
-    // TODO(jordynass): Have this request a change in the feature's status via
-    // the browserProxy.
+
+    // Pass the negation of |this.checked_|: this indicates that if the toggle
+    // is checked, the intent is for it to be unchecked, and vice versa.
+    this.fire(
+        'feature-toggle-clicked',
+        {feature: this.feature, enabled: !this.checked_});
   },
 });
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
index 05bd3d2b..0521c82 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.html
@@ -8,6 +8,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../route.html">
+<link rel="import" href="../controls/password_prompt_dialog.html">
 <link rel="import" href="../settings_page/settings_animated_pages.html">
 <link rel="import" href="../settings_page/settings_subpage.html">
 <link rel="import" href="../settings_shared_css.html">
@@ -64,6 +65,13 @@
         </settings-subpage>
       </template>
     </settings-animated-pages>
+
+    <template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp>
+      <settings-password-prompt-dialog
+          id="passwordPrompt" on-close="onPasswordPromptDialogClose_"
+          auth-token="{{authToken_}}">
+      </settings-password-prompt-dialog>
+    </template>
   </template>
   <script src="multidevice_page.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
index 1f8e3a61..8c32b45 100644
--- a/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/multidevice_page/multidevice_page.js
@@ -31,6 +31,31 @@
         return map;
       },
     },
+
+    /**
+     * Authentication token provided by password-prompt-dialog.
+     * @private {string}
+     */
+    authToken_: {
+      type: String,
+      value: '',
+      observer: 'authTokenChanged_',
+    },
+
+    /**
+     * The feature which the user wishes to enable, but which requires
+     * authentication. Once |authToken_| is valid, this value will be passed
+     * along to |browserProxy_|, and subsequently cleared.
+     * @private {?settings.MultiDeviceFeature}
+     */
+    attemptedEnabledFeature_: {type: Number, value: null},
+
+    /** @private {boolean} */
+    showPasswordPromptDialog_: {type: Boolean, value: false},
+  },
+
+  listeners: {
+    'feature-toggle-clicked': 'onFeatureToggleClicked_',
   },
 
   /** @private {?settings.MultiDeviceBrowserProxy} */
@@ -130,11 +155,89 @@
         this.browserProxy_.showMultiDeviceSetupDialog();
         return;
       case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER:
-        // Intentional fall-through.
+      // Intentional fall-through.
       case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION:
         // If this device is waiting for action on the server or the host
         // device, clicking the button should trigger this action.
         this.browserProxy_.retryPendingHostSetup();
     }
   },
+
+  /**
+   * Tell |this.browserProxy_| to enable |this.attemptedEnabledFeature_|.
+   * @private
+   */
+  enableAttemptedFeature_: function() {
+    if (this.attemptedEnabledFeature_ != null) {
+      this.browserProxy_.setFeatureEnabledState(
+          this.attemptedEnabledFeature_, true /* enabled */, this.authToken_);
+      this.attemptedEnabledFeature_ = null;
+    }
+  },
+
+  /** @private */
+  openPasswordPromptDialog_: function() {
+    this.showPasswordPromptDialog_ = true;
+  },
+
+  /** @private */
+  onPasswordPromptDialogClose_: function() {
+    this.showPasswordPromptDialog_ = false;
+  },
+
+  /**
+   * Attempt to enable the provided feature. If not authenticated (i.e.,
+   * |authToken_| is invalid), display the password prompt to begin the
+   * authentication process.
+   *
+   * @param {!{detail: !Object}} event
+   * @private
+   */
+  onFeatureToggleClicked_: function(event) {
+    let feature = event.detail.feature;
+    let enabled = event.detail.enabled;
+
+    // Authentication is never required if disabling a feature; only consider it
+    // if enabling.
+    if (enabled) {
+      // TODO(crbug.com/876436): Actually determine this, when the API is
+      // available. It's fine to default to true here for now, because it errs
+      // on the side of more security.
+      let isSmartLockPrefEnabled = true;
+
+      let isPasswordPromptRequired =
+          (feature === settings.MultiDeviceFeature.SMART_LOCK) ||
+          (feature === settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE &&
+           isSmartLockPrefEnabled);
+
+      if (isPasswordPromptRequired) {
+        this.attemptedEnabledFeature_ = feature;
+
+        if (this.authToken_) {
+          this.enableAttemptedFeature_();
+        } else {
+          this.openPasswordPromptDialog_();
+        }
+        return;
+      }
+    }
+
+    // No authentication is required.
+    this.browserProxy_.setFeatureEnabledState(feature, enabled);
+  },
+
+  /**
+   * Called when the authToken_ changes. If the authToken is valid, that
+   * indicates the user authenticated successfully. If not, cancel the pending
+   * attempt to enable attemptedEnabledFeature_.
+   * @param {String} authToken
+   * @private
+   */
+  authTokenChanged_: function(authToken) {
+    if (this.authToken_) {
+      this.enableAttemptedFeature_();
+    } else {
+      this.attemptedEnabledFeature_ = null;
+    }
+  },
 });
diff --git a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
index c7e32399..762f2aa 100644
--- a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
+++ b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
@@ -14,9 +14,12 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
 #include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "components/ukm/content/source_url_recorder.h"
+#include "components/ukm/test_ukm_recorder.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -111,14 +114,15 @@
   std::string script = base::StringPrintf(
       "%s('%s','%s');", ad_script ? "createAdFrame" : "createFrame",
       url.spec().c_str(), name.c_str());
-
-  content::TestNavigationObserver navigation_observer(GetWebContents(), 1);
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(rfh);
+  content::TestNavigationObserver navigation_observer(web_contents, 1);
   EXPECT_TRUE(content::ExecuteScript(rfh, script));
   navigation_observer.Wait();
   EXPECT_TRUE(navigation_observer.last_navigation_succeeded())
       << navigation_observer.last_net_error_code();
   return content::FrameMatchingPredicate(
-      GetWebContents(), base::BindRepeating(&content::FrameMatchesName, name));
+      web_contents, base::BindRepeating(&content::FrameMatchesName, name));
 }
 
 content::RenderFrameHost* AdTaggingBrowserTest::CreateDocWrittenFrameImpl(
@@ -126,18 +130,19 @@
     bool ad_script) {
   content::RenderFrameHost* rfh = adapter.render_frame_host();
   std::string name = GetUniqueFrameName();
-
   std::string script = base::StringPrintf(
       "%s('%s', '%s');",
       ad_script ? "createDocWrittenAdFrame" : "createDocWrittenFrame",
       name.c_str(), GetURL("").spec().c_str());
-  content::TestNavigationObserver navigation_observer(GetWebContents(), 1);
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(rfh);
+  content::TestNavigationObserver navigation_observer(web_contents, 1);
   EXPECT_TRUE(content::ExecuteScript(rfh, script));
   navigation_observer.Wait();
   EXPECT_TRUE(navigation_observer.last_navigation_succeeded())
       << navigation_observer.last_net_error_code();
   return content::FrameMatchingPredicate(
-      GetWebContents(), base::BindRepeating(&content::FrameMatchesName, name));
+      web_contents, base::BindRepeating(&content::FrameMatchesName, name));
 }
 
 // Given a RenderFrameHost, navigates the page to the given |url| and waits
@@ -320,6 +325,110 @@
   EXPECT_TRUE(*observer.GetIsAdSubframe(ad_frame->GetFrameTreeNodeId()));
 }
 
+const ukm::mojom::UkmEntry* FindDocumentCreatedEntry(
+    const ukm::TestUkmRecorder& ukm_recorder,
+    ukm::SourceId source_id) {
+  auto entries =
+      ukm_recorder.GetEntriesByName(ukm::builders::DocumentCreated::kEntryName);
+
+  for (auto* entry : entries) {
+    if (entry->source_id == source_id)
+      return entry;
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
+void ExpectLatestUkmEntry(const ukm::TestUkmRecorder& ukm_recorder,
+                          size_t expected_num_entries,
+                          base::StringPiece metric_name,
+                          bool from_main_frame,
+                          const GURL& main_frame_url,
+                          int64_t expected_value) {
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::AbusiveExperienceHeuristic::kEntryName);
+  EXPECT_EQ(expected_num_entries, entries.size());
+
+  // Check that the event is keyed to |main_frame_url| only if it was from the
+  // top frame.
+  if (from_main_frame) {
+    ukm_recorder.ExpectEntrySourceHasUrl(entries.back(), main_frame_url);
+  } else {
+    EXPECT_FALSE(ukm_recorder.GetSourceForSourceId(entries.back()->source_id));
+  }
+
+  // Check that a DocumentCreated entry was created, and it's keyed to
+  // |main_frame_url| only if it was from the top frame. However, we can always
+  // use the navigation source ID to link this source to |main_frame_url|.
+  const ukm::mojom::UkmEntry* dc_entry =
+      FindDocumentCreatedEntry(ukm_recorder, entries.back()->source_id);
+  EXPECT_EQ(entries.back()->source_id, dc_entry->source_id);
+  if (from_main_frame) {
+    ukm_recorder.ExpectEntrySourceHasUrl(dc_entry, main_frame_url);
+  } else {
+    EXPECT_FALSE(ukm_recorder.GetSourceForSourceId(dc_entry->source_id));
+  }
+
+  const ukm::UkmSource* navigation_source =
+      ukm_recorder.GetSourceForSourceId(*ukm_recorder.GetEntryMetric(
+          dc_entry, ukm::builders::DocumentCreated::kNavigationSourceIdName));
+  EXPECT_EQ(main_frame_url, navigation_source->url());
+
+  EXPECT_EQ(from_main_frame,
+            *ukm_recorder.GetEntryMetric(
+                dc_entry, ukm::builders::DocumentCreated::kIsMainFrameName));
+
+  ukm_recorder.ExpectEntryMetric(entries.back(), metric_name, expected_value);
+}
+
+IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, WindowOpenFromSubframe) {
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  GURL main_frame_url =
+      embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
+  ui_test_utils::NavigateToURL(browser(), main_frame_url);
+  content::WebContents* main_tab = GetWebContents();
+
+  size_t expected_num_entries = 0;
+  for (bool cross_origin : {false, true}) {
+    for (bool ad_frame : {false, true}) {
+      std::string hostname = cross_origin ? "b.com" : "a.com";
+      std::string suffix = ad_frame ? "&ad=true" : "";
+      SCOPED_TRACE(::testing::Message()
+                   << "cross_origin = " << cross_origin << ", "
+                   << "ad_frame = " << ad_frame);
+      RenderFrameHost* child = CreateSrcFrame(
+          main_tab, embedded_test_server()->GetURL(
+                        hostname, "/ad_tagging/frame_factory.html?1" + suffix));
+      EXPECT_TRUE(content::ExecuteScript(child, "window.open();"));
+      ExpectLatestUkmEntry(ukm_recorder, ++expected_num_entries,
+                           ukm::builders::AbusiveExperienceHeuristic::
+                               kDidWindowOpenFromAdSubframeName,
+                           false /* from_main_frame */, main_frame_url,
+                           ad_frame);
+    }
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, WindowOpenWithScriptInStack) {
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  GURL main_frame_url = GetURL("frame_factory.html");
+  ui_test_utils::NavigateToURL(browser(), main_frame_url);
+  content::WebContents* main_tab = GetWebContents();
+
+  EXPECT_TRUE(content::ExecuteScript(main_tab, "windowOpenFromNonAdScript();"));
+  ExpectLatestUkmEntry(
+      ukm_recorder, 1 /* expected_num_entries */,
+      ukm::builders::AbusiveExperienceHeuristic::kDidWindowOpenFromAdScriptName,
+      true /* from_main_frame */, main_frame_url, false /* expected_value */);
+
+  EXPECT_TRUE(content::ExecuteScript(main_tab, "windowOpenFromAdScript();"));
+  ExpectLatestUkmEntry(
+      ukm_recorder, 2 /* expected_num_entries */,
+      ukm::builders::AbusiveExperienceHeuristic::kDidWindowOpenFromAdScriptName,
+      true /* from_main_frame */, main_frame_url, true /* expected_value */);
+}
+
 }  // namespace
 
 }  // namespace subresource_filter
diff --git a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
index c5575cc..4d33af44 100644
--- a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
+++ b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
@@ -107,7 +107,7 @@
     Java_AutofillPopupBridge_addToAutofillSuggestionArray(
         env, data_array, i, value, label, android_icon_id,
         /*icon_at_start=*/false, suggestion.frontend_id, is_deletable,
-        is_label_multiline, suggestion.is_value_bold);
+        is_label_multiline, /*isLabelBold*/ false);
   }
 
   Java_AutofillPopupBridge_show(env, java_object_, data_array,
diff --git a/chrome/browser/ui/ash/ash_util.cc b/chrome/browser/ui/ash/ash_util.cc
index e701e4e..07f3253 100644
--- a/chrome/browser/ui/ash/ash_util.cc
+++ b/chrome/browser/ui/ash/ash_util.cc
@@ -23,7 +23,7 @@
 
 bool IsAcceleratorDeprecated(const ui::Accelerator& accelerator) {
   // When running in mash the browser doesn't handle ash accelerators.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsMultiProcessMash())
     return false;
 
   return ash::Shell::Get()->accelerator_controller()->IsDeprecated(accelerator);
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 6cfb3d21..7496085 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -143,12 +143,14 @@
 
 void ChromeBrowserMainExtraPartsAsh::ServiceManagerConnectionStarted(
     content::ServiceManagerConnection* connection) {
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsMultiProcessMash()) {
     // ash::Shell will not be created because ash is running out-of-process.
     ash::Shell::SetIsBrowserProcessWithMash();
   }
+
   if (features::IsUsingWindowService()) {
     // Start up the window service and the ash system UI service.
+    // NOTE: ash::Shell is still created below for SingleProcessMash.
     connection->GetConnector()->StartService(
         service_manager::Identity(ui::mojom::kServiceName));
     connection->GetConnector()->StartService(
@@ -180,14 +182,18 @@
       std::make_unique<NetworkConnectDelegateChromeOS>();
   chromeos::NetworkConnect::Initialize(network_connect_delegate_.get());
 
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsMultiProcessMash()) {
     ash_shell_init_ = std::make_unique<AshShellInit>();
   } else {
+    // TODO(jamescook): Sort out whether to use ImmersiveContextAsh or
+    // ImmersiveContextMus in SingleProcessMash.
     immersive_context_ = std::make_unique<ImmersiveContextMus>();
     immersive_handler_factory_ = std::make_unique<ImmersiveHandlerFactoryMus>();
 
     // Enterprise support in the browser can monitor user activity. Connect to
     // the UI service to monitor activity. The ash process has its own monitor.
+    // TODO(jamescook): Figure out if we need this for SingleProcessMash.
+    // https://crbug.com/626899
     user_activity_detector_ = std::make_unique<ui::UserActivityDetector>();
     ui::mojom::UserActivityMonitorPtr user_activity_monitor;
     content::ServiceManagerConnection::GetForProcess()
@@ -261,8 +267,9 @@
             detector);
   }
 
-  // TODO(mash): Port TabScrubber.
-  if (features::IsAshInBrowserProcess()) {
+  // TODO(mash): Port TabScrubber. This depends on where gesture recognition
+  // happens because TabScrubber uses 3-finger scrolls. https://crbug.com/796366
+  if (!features::IsUsingWindowService()) {
     // Initialize TabScrubber after the Ash Shell has been initialized.
     TabScrubber::GetInstance();
   }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 9339518..65188b4 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -998,7 +998,7 @@
                : keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED);
   }
   // TODO(crbug.com/557406): Fix this interaction pattern in Mash.
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsMultiProcessMash()) {
     const bool is_enabled = keyboard::IsKeyboardEnabled();
     if (was_enabled && !is_enabled)
       ash::Shell::Get()->DisableKeyboard();
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager.cc
index 0818662..07aedc9e 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager.cc
@@ -26,7 +26,7 @@
   DCHECK(!g_multi_user_window_manager_instance);
   // TODO(crbug.com/557406): Enable this component in Mash. The object itself
   // has direct ash dependencies.
-  if (features::IsAshInBrowserProcess() &&
+  if (!features::IsUsingWindowService() &&
       SessionControllerClient::IsMultiProfileAvailable()) {
     MultiUserWindowManagerChromeOS* manager =
         new MultiUserWindowManagerChromeOS(
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
index bdf79d1..3415bf5 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc
@@ -713,7 +713,7 @@
   if (visible) {
     // TODO(erg): When we get rid of the classic ash, get rid of the direct
     // linkage on tablet_mode_controller() here.
-    if (!features::IsAshInBrowserProcess()) {
+    if (features::IsUsingWindowService()) {
       aura::WindowTreeHostMus::ForWindow(window)->PerformWmAction(
           ash::mojom::kAddWindowToTabletMode);
     } else {
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc
index 37b2acfe..67da483 100644
--- a/chrome/browser/ui/ash/system_tray_client.cc
+++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -180,7 +180,7 @@
   // Place the dialog in the appropriate modal dialog container, either above
   // or below the lock screen, based on the login state.
   int container_id = GetDialogParentContainerId();
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     using ui::mojom::WindowManager;
     params.mus_properties[WindowManager::kContainerId_InitProperty] =
         mojo::ConvertTo<std::vector<uint8_t>>(container_id);
diff --git a/chrome/browser/ui/ash/tab_scrubber.cc b/chrome/browser/ui/ash/tab_scrubber.cc
index 48dc98b..8b975d1 100644
--- a/chrome/browser/ui/ash/tab_scrubber.cc
+++ b/chrome/browser/ui/ash/tab_scrubber.cc
@@ -90,7 +90,7 @@
       weak_ptr_factory_(this) {
   // TODO(mash): Add window server API to observe swipe gestures. Observing
   // gestures on browser windows is not sufficient, as this feature works when
-  // the cursor is over the shelf, desktop, etc.
+  // the cursor is over the shelf, desktop, etc. https://crbug.com/796366
   ash::Shell::Get()->AddPreTargetHandler(this);
   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSED,
                  content::NotificationService::AllSources());
diff --git a/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.cc b/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.cc
index 4a74a0c..c1154d3 100644
--- a/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.cc
+++ b/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.cc
@@ -10,14 +10,17 @@
 #include "chrome/common/channel_info.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/accessibility/ax_window_obj_wrapper.h"
 
-AXRootObjWrapper::AXRootObjWrapper() : alert_window_(new aura::Window(NULL)) {
+AXRootObjWrapper::AXRootObjWrapper()
+    : alert_window_(std::make_unique<aura::Window>(nullptr)) {
   alert_window_->Init(ui::LAYER_NOT_DRAWN);
+  aura::Env::GetInstance()->AddObserver(this);
 
   if (display::Screen::GetScreen())
     display::Screen::GetScreen()->AddObserver(this);
@@ -27,10 +30,13 @@
   if (display::Screen::GetScreen())
     display::Screen::GetScreen()->RemoveObserver(this);
 
-  if (alert_window_) {
-    delete alert_window_;
-    alert_window_ = NULL;
-  }
+  // If alert_window_ is nullptr already, that means OnWillDestroyEnv
+  // was already called, so we shouldn't call RemoveObserver(this) again.
+  if (!alert_window_)
+    return;
+
+  aura::Env::GetInstance()->RemoveObserver(this);
+  alert_window_.reset();
 }
 
 views::AXAuraObjWrapper* AXRootObjWrapper::GetAlertForText(
@@ -38,7 +44,8 @@
   alert_window_->SetTitle(base::UTF8ToUTF16((text)));
   views::AXWindowObjWrapper* window_obj =
       static_cast<views::AXWindowObjWrapper*>(
-          views::AXAuraObjCache::GetInstance()->GetOrCreate(alert_window_));
+          views::AXAuraObjCache::GetInstance()->GetOrCreate(
+              alert_window_.get()));
   window_obj->set_is_alert(true);
   return window_obj;
 }
@@ -61,7 +68,7 @@
     std::vector<views::AXAuraObjWrapper*>* out_children) {
   views::AXAuraObjCache::GetInstance()->GetTopLevelWindows(out_children);
   out_children->push_back(
-      views::AXAuraObjCache::GetInstance()->GetOrCreate(alert_window_));
+      views::AXAuraObjCache::GetInstance()->GetOrCreate(alert_window_.get()));
 }
 
 void AXRootObjWrapper::Serialize(ui::AXNodeData* out_node_data) {
@@ -95,3 +102,10 @@
   AutomationManagerAura::GetInstance()->OnEvent(
       this, ax::mojom::Event::kLocationChanged);
 }
+
+void AXRootObjWrapper::OnWindowInitialized(aura::Window* window) {}
+
+void AXRootObjWrapper::OnWillDestroyEnv() {
+  alert_window_.reset();
+  aura::Env::GetInstance()->RemoveObserver(this);
+}
diff --git a/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.h b/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.h
index 932b07f..a9bae656 100644
--- a/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.h
+++ b/chrome/browser/ui/aura/accessibility/ax_root_obj_wrapper.h
@@ -7,10 +7,12 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
 #include "ui/accessibility/platform/ax_unique_id.h"
+#include "ui/aura/env_observer.h"
 #include "ui/display/display_observer.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
 
@@ -21,7 +23,8 @@
 // Provides the root AX desktop node for the chrome.automation.getDesktop() API
 // call. Each top-level desktop window is a child.
 class AXRootObjWrapper : public views::AXAuraObjWrapper,
-                         display::DisplayObserver {
+                         display::DisplayObserver,
+                         aura::EnvObserver {
  public:
   AXRootObjWrapper();
   ~AXRootObjWrapper() override;
@@ -45,9 +48,13 @@
   void OnDisplayMetricsChanged(const display::Display& display,
                                uint32_t changed_metrics) override;
 
+  // aura::EnvObserver:
+  void OnWindowInitialized(aura::Window* window) override;
+  void OnWillDestroyEnv() override;
+
   ui::AXUniqueId unique_id_;
 
-  aura::Window* alert_window_;
+  std::unique_ptr<aura::Window> alert_window_;
 
   DISALLOW_COPY_AND_ASSIGN(AXRootObjWrapper);
 };
diff --git a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_interactive_uitest.mm b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_interactive_uitest.mm
index 6edf39c..3e53254a 100644
--- a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_interactive_uitest.mm
+++ b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_interactive_uitest.mm
@@ -110,7 +110,8 @@
 };
 
 // Test that switching to a packaged app changes window cycling behavior.
-IN_PROC_BROWSER_TEST_F(AppShimMenuControllerUITest, WindowCycling) {
+// Disabled due to flakiness. https://crbug.com/876623
+IN_PROC_BROWSER_TEST_F(AppShimMenuControllerUITest, DISABLED_WindowCycling) {
   EXPECT_FALSE([app1_->GetNativeWindow() isMainWindow]);
   EXPECT_TRUE(ShowAndFocusNativeWindow(app1_->GetNativeWindow()));
   ExpectActiveWithCounts(app1_->GetNativeWindow(), 1, 0, 0, 0);
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index 4b403358..59f56d7 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -233,7 +233,7 @@
   if (IsFrameless())
     return true;
 
-  return HasFrameColor() && features::IsAshInBrowserProcess();
+  return HasFrameColor() && !features::IsUsingWindowService();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -331,7 +331,7 @@
   if (IsFrameless())
     return CreateNonStandardAppFrame();
 
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return nullptr;
 
   ash::NonClientFrameViewAsh* custom_frame_view =
@@ -395,7 +395,7 @@
   SkRegion* draggable_region = GetDraggableRegion();
   // Set the NativeAppWindow's draggable region on the mus window.
   if (draggable_region && !draggable_region->isEmpty() && widget() &&
-      !features::IsAshInBrowserProcess()) {
+      features::IsUsingWindowService()) {
     // Supply client area insets that encompass all draggable regions.
     gfx::Insets insets(draggable_region->getBounds().bottom(), 0, 0, 0);
 
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
index e05c9ebb..ce8a15ae 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
@@ -38,6 +38,7 @@
   static const SkColor kWarningColor;
 
   // Horizontal spacing between value and description in the row.
+  // TODO(crbug.com/876364): Replace this with a global constant.
   static const int kValueLabelPadding = 24;
 
   static int GetCornerRadius();
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 4e69f5ac..92e1f59b 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -166,8 +166,8 @@
   // The description view can be nullptr.
   virtual views::View* CreateDescriptionLabel();
 
-  // Creates a description label iff the description text isn't empty.
-  views::Label* CreateDescriptionLabelInternal() const;
+  // Creates a label matching the style of the description label.
+  views::Label* CreateSecondaryLabel(const base::string16& text) const;
 
  private:
   void AddIcon(gfx::ImageSkia icon);
@@ -384,7 +384,7 @@
 
   AddChildView(CreateValueLabel());
 
-  AddSpacerWithSize(views::MenuConfig::instance().item_horizontal_padding,
+  AddSpacerWithSize(AutofillPopupBaseView::kValueLabelPadding,
                     /*resize=*/true, layout_manager);
 
   views::View* description_label = CreateDescriptionLabel();
@@ -405,6 +405,12 @@
 
 views::View* AutofillPopupItemView::CreateValueLabel() {
   // TODO(crbug.com/831603): Remove elision responsibilities from controller.
+  base::string16 text =
+      popup_view_->controller()->GetElidedValueAt(line_number_);
+  if (popup_view_->controller()
+          ->GetSuggestionAt(line_number_)
+          .is_value_secondary)
+    return CreateSecondaryLabel(text);
   views::Label* text_label = new views::Label(
       popup_view_->controller()->GetElidedValueAt(line_number_),
       {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
@@ -416,24 +422,21 @@
 }
 
 views::View* AutofillPopupItemView::CreateDescriptionLabel() {
-  return CreateDescriptionLabelInternal();
+  base::string16 text =
+      popup_view_->controller()->GetElidedLabelAt(line_number_);
+  return text.empty() ? nullptr : CreateSecondaryLabel(text);
 }
 
-views::Label* AutofillPopupItemView::CreateDescriptionLabelInternal() const {
-  const base::string16& description_text =
-      popup_view_->controller()->GetElidedLabelAt(line_number_);
-  if (!description_text.empty()) {
-    views::Label* subtext_label = new views::Label(
-        description_text,
-        {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
-                               ChromeTextStyle::STYLE_SECONDARY)});
-    subtext_label->SetEnabledColor(views::style::GetColor(
-        *this, ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
-        ChromeTextStyle::STYLE_SECONDARY));
+views::Label* AutofillPopupItemView::CreateSecondaryLabel(
+    const base::string16& text) const {
+  views::Label* subtext_label = new views::Label(
+      text, {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+                                   ChromeTextStyle::STYLE_SECONDARY)});
+  subtext_label->SetEnabledColor(
+      views::style::GetColor(*this, ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+                             ChromeTextStyle::STYLE_SECONDARY));
 
-    return subtext_label;
-  }
-  return nullptr;
+  return subtext_label;
 }
 
 void AutofillPopupItemView::AddIcon(gfx::ImageSkia icon) {
@@ -496,14 +499,22 @@
 
 views::View* PasswordPopupSuggestionView::CreateValueLabel() {
   views::View* label = AutofillPopupSuggestionView::CreateValueLabel();
+  // Empty username is rendered as a secondary text. It doesn't need to be
+  // truncated.
+  if (popup_view_->controller()
+          ->GetSuggestionAt(line_number_)
+          .is_value_secondary)
+    return label;
   return new ConstrainedWidthView(label, kAutofillPopupUsernameMaxWidth);
 }
 
 views::View* PasswordPopupSuggestionView::CreateDescriptionLabel() {
-  views::Label* label = CreateDescriptionLabelInternal();
-  if (!label)
+  base::string16 text =
+      popup_view_->controller()->GetElidedLabelAt(line_number_);
+  if (text.empty())
     return nullptr;
 
+  views::Label* label = CreateSecondaryLabel(text);
   label->SetElideBehavior(gfx::TRUNCATE);
   return new ConstrainedWidthView(label, kAutofillPopupPasswordMaxWidth);
 }
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc
index 99584956..ef05d40 100644
--- a/chrome/browser/ui/views/download/download_shelf_view.cc
+++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -137,7 +137,7 @@
   if (download_views_.empty())
     Close(AUTOMATIC);
   else if (CanAutoClose())
-    mouse_watcher_.Start();
+    mouse_watcher_.Start(GetWidget()->GetNativeWindow());
   Layout();
   SchedulePaint();
 }
@@ -174,7 +174,7 @@
 
 void DownloadShelfView::OpenedDownload() {
   if (CanAutoClose())
-    mouse_watcher_.Start();
+    mouse_watcher_.Start(GetWidget()->GetNativeWindow());
 }
 
 content::PageNavigator* DownloadShelfView::GetNavigator() {
diff --git a/chrome/browser/ui/views/frame/browser_frame_ash.cc b/chrome/browser/ui/views/frame/browser_frame_ash.cc
index f55099b..f93f32d 100644
--- a/chrome/browser/ui/views/frame/browser_frame_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_ash.cc
@@ -8,7 +8,7 @@
 
 // This file is only instantiated in classic ash/mus. It is never used in mash.
 // See native_browser_frame_factory_chromeos.cc switches on
-// features::IsAshInBrowserProcess().
+// features::IsUsingWindowService().
 #include "ash/public/cpp/window_properties.h"
 #include "ash/public/cpp/window_state_type.h"
 #include "ash/shell.h"                     // mash-ok
@@ -63,7 +63,7 @@
                                  BrowserView* browser_view)
     : views::NativeWidgetAura(browser_frame),
       browser_view_(browser_view) {
-  DCHECK(features::IsAshInBrowserProcess());
+  DCHECK(!features::IsUsingWindowService());
   GetNativeWindow()->SetName("BrowserFrameAsh");
   Browser* browser = browser_view->browser();
   ash::wm::WindowState* window_state =
diff --git a/chrome/browser/ui/views/frame/browser_frame_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_frame_ash_browsertest.cc
index a7cc5d9..d337f67 100644
--- a/chrome/browser/ui/views/frame/browser_frame_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_ash_browsertest.cc
@@ -83,7 +83,7 @@
   Browser* browser = new Browser(params);
   browser->window()->Show();
 
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     WidgetBoundsWatcher watch(
         BrowserView::GetBrowserViewForBrowser(browser)->GetWidget(),
         original_bounds);
@@ -114,7 +114,7 @@
     expectation.set_y(original_bounds.y());
   }
 
-  if (!features::IsAshInBrowserProcess()) {
+  if (features::IsUsingWindowService()) {
     WidgetBoundsWatcher watch(
         BrowserView::GetBrowserViewForBrowser(browser)->GetWidget(),
         expectation);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 9353204..ee183e9 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -318,6 +318,11 @@
   if (!MD::IsRefreshUi())
     return true;
 
+  // In single-tab mode, the whole point is to have the active tab blend with
+  // the frame.
+  if (ShouldPaintAsSingleTabMode())
+    return false;
+
   // Refresh normally avoids strokes and relies on the active tab contrasting
   // sufficiently with the frame background.  When there isn't enough contrast,
   // fall back to a stroke.  Always compute the contrast ratio against the
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 1c7b553..909d7cd 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -389,9 +389,9 @@
 // tablet mode being toggled.
 IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewAshTest,
                        ToggleTabletModeRelayout) {
-  // For OopAsh, this test is covered by
+  // For mash, this test is covered by
   // CustomFrameViewAshTest.ToggleTabletModeRelayout.
-  if (!features::IsAshInBrowserProcess())
+  if (features::IsUsingWindowService())
     return;
 
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
@@ -480,7 +480,7 @@
       static_cast<BrowserNonClientFrameViewAsh*>(
           widget->non_client_view()->frame_view());
 
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     ash::FrameCaptionButtonContainerView::TestApi test(
         frame_view->caption_button_container_);
     EXPECT_TRUE(test.size_button()->icon_definition_for_test());
@@ -994,9 +994,9 @@
 // Regression test for https://crbug.com/839955
 IN_PROC_BROWSER_TEST_P(HostedAppNonClientFrameViewAshTest,
                        ActiveStateOfButtonMatchesWidget) {
-  // The caption button part of this test is covered for OopAsh by
+  // The caption button part of this test is covered for mash by
   // NonClientFrameViewAshTest::ActiveStateOfButtonMatchesWidget.
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     ash::FrameCaptionButtonContainerView::TestApi test(
         GetFrameViewAsh(browser_view_)->caption_button_container_);
     EXPECT_TRUE(test.size_button()->paint_as_active());
@@ -1004,7 +1004,7 @@
   EXPECT_TRUE(GetPaintingAsActive());
 
   browser_view_->GetWidget()->Deactivate();
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     ash::FrameCaptionButtonContainerView::TestApi test(
         GetFrameViewAsh(browser_view_)->caption_button_container_);
     EXPECT_FALSE(test.size_button()->paint_as_active());
@@ -1034,7 +1034,7 @@
 IN_PROC_BROWSER_TEST_P(BrowserNonClientFrameViewAshBackButtonTest,
                        V1BackButton) {
   // Normal browser windows don't have a frame back button.
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     BrowserNonClientFrameViewAsh* frame_view =
         GetFrameViewAsh(BrowserView::GetBrowserViewForBrowser(browser()));
     EXPECT_FALSE(frame_view->back_button_);
@@ -1060,7 +1060,7 @@
   BrowserNonClientFrameViewAsh* frame_view = GetFrameViewAsh(browser_view);
   aura::Window* app_window = frame_view->GetWidget()->GetNativeWindow();
 
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     ASSERT_TRUE(frame_view->back_button_);
     EXPECT_TRUE(frame_view->back_button_->visible());
     // The back button should be disabled initially.
@@ -1075,7 +1075,7 @@
   NavigateParams nav_params(browser, kAppStartURL, ui::PAGE_TRANSITION_LINK);
   ui_test_utils::NavigateToURL(&nav_params);
 
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     EXPECT_TRUE(frame_view->back_button_->enabled());
   } else {
     EXPECT_EQ(ash::FrameBackButtonState::kEnabled,
@@ -1084,7 +1084,7 @@
 
   // Go back to the blank. The back button should be disabled again.
   chrome::GoBack(browser, WindowOpenDisposition::CURRENT_TAB);
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     EXPECT_FALSE(frame_view->back_button_->enabled());
   } else {
     EXPECT_EQ(ash::FrameBackButtonState::kDisabled,
@@ -1304,8 +1304,8 @@
 
 IN_PROC_BROWSER_TEST_P(HomeLauncherBrowserNonClientFrameViewAshTest,
                        TabletModeBrowserCaptionButtonVisibility) {
-  // For OopAsh, this is tested by an ash unit test of the same name.
-  if (!features::IsAshInBrowserProcess())
+  // For mash, this is tested by an ash unit test of the same name.
+  if (features::IsUsingWindowService())
     return;
 
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
@@ -1331,8 +1331,8 @@
 
 IN_PROC_BROWSER_TEST_P(HomeLauncherBrowserNonClientFrameViewAshTest,
                        TabletModeAppCaptionButtonVisibility) {
-  // For OopAsh, this is tested by an ash unit test of the same name.
-  if (!features::IsAshInBrowserProcess())
+  // For mash, this is tested by an ash unit test of the same name.
+  if (features::IsUsingWindowService())
     return;
 
   browser()->window()->Close();
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index be0586d..d42057c 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -113,7 +113,7 @@
       browser_view_->top_container());
 
   observed_windows_.Add(
-      features::IsAshInBrowserProcess()
+      !features::IsUsingWindowService()
           ? browser_view_->GetNativeWindow()
           : browser_view_->GetNativeWindow()->GetRootWindow());
 }
@@ -206,7 +206,7 @@
 }
 
 void ImmersiveModeControllerAsh::CreateMashRevealWidget() {
-  if (features::IsAshInBrowserProcess())
+  if (!features::IsUsingWindowService())
     return;
 
   DCHECK(!mash_reveal_widget_);
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
index 172b5ed..fdcc404 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_browsertest.cc
@@ -266,8 +266,8 @@
   ASSERT_NO_FATAL_FAILURE(test::SetAndWaitForTabletMode(false));
   EXPECT_FALSE(controller()->IsEnabled());
 
-  // TODO(estade): make kTopviewInset work in OopAsh.
-  if (features::IsAshInBrowserProcess())
+  // TODO(estade): make kTopviewInset work in mash.
+  if (!features::IsUsingWindowService())
     EXPECT_GT(aura_window->GetProperty(aura::client::kTopViewInset), 0);
 }
 
@@ -275,10 +275,10 @@
 // tablet mode.
 IN_PROC_BROWSER_TEST_F(ImmersiveModeControllerAshHostedAppBrowserTest,
                        FrameLayoutToggleTabletMode) {
-  // For OopAsh, the layout is handled in Ash and tested by
+  // For mash, the layout is handled in Ash and tested by
   // FrameCaptionButtonContainerViewTest.
-  // TODO(estade): remove this test when OopAsh is default.
-  if (!features::IsAshInBrowserProcess())
+  // TODO(estade): remove this test when mash is default.
+  if (features::IsUsingWindowService())
     return;
 
   LaunchAppBrowser();
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 8571a74..f43bfcfb 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -235,6 +235,11 @@
   play_pause_controls_view_->SetToggled(controller_->IsPlayerActive());
   play_pause_controls_view_->set_owned_by_client();
 
+  // views::View that closes the window. --------------------------------------
+  close_controls_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
+  close_controls_view_->layer()->SetFillsBoundsOpaquely(false);
+  close_controls_view_->set_owned_by_client();
+
   UpdatePlayPauseControlsSize();
 
   // Accessibility.
@@ -256,14 +261,13 @@
   controls_parent_view_->SetSize(GetBounds().size());
   controls_parent_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
   controls_parent_view_->AddChildView(play_pause_controls_view_.get());
-  controls_parent_view_->AddChildView(close_controls_view_.get());
-  close_controls_view_->set_owned_by_client();
   controls_parent_view_->layer()->SetFillsBoundsOpaquely(false);
   controls_parent_view_->set_owned_by_client();
 
   // Add as child views to this widget. ---------------------------------------
   GetContentsView()->AddChildView(controls_background_view_.get());
   GetContentsView()->AddChildView(controls_parent_view_.get());
+  GetContentsView()->AddChildView(close_controls_view_.get());
 
   // Paint to ui::Layers. -----------------------------------------------------
   video_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
@@ -300,6 +304,7 @@
 
 void OverlayWindowViews::UpdateControlsVisibility(bool is_visible) {
   GetControlsBackgroundLayer()->SetVisible(is_visible);
+  GetCloseControlsLayer()->SetVisible(is_visible);
   GetControlsParentLayer()->SetVisible(is_visible);
 }
 
@@ -531,6 +536,10 @@
   return controls_background_view_->layer();
 }
 
+ui::Layer* OverlayWindowViews::GetCloseControlsLayer() {
+  return close_controls_view_->layer();
+}
+
 ui::Layer* OverlayWindowViews::GetControlsParentLayer() {
   return controls_parent_view_->layer();
 }
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h
index edc46259..bf41278 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.h
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -119,6 +119,7 @@
   void SetSecondCustomControlsBounds();
 
   ui::Layer* GetControlsBackgroundLayer();
+  ui::Layer* GetCloseControlsLayer();
   ui::Layer* GetControlsParentLayer();
 
   // Toggles the play/pause control through the |controller_| and updates the
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index dbb6856f..174db76 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -221,21 +221,21 @@
 // the escape key.
 class EscapeTracker : public ui::EventHandler {
  public:
-  explicit EscapeTracker(const base::Closure& callback)
-      : escape_callback_(callback),
-        event_monitor_(views::EventMonitor::CreateApplicationMonitor(this)) {
-  }
+  EscapeTracker(base::OnceClosure callback, gfx::NativeWindow context)
+      : escape_callback_(std::move(callback)),
+        event_monitor_(
+            views::EventMonitor::CreateApplicationMonitor(this, context)) {}
 
  private:
   // ui::EventHandler:
   void OnKeyEvent(ui::KeyEvent* key) override {
     if (key->type() == ui::ET_KEY_PRESSED &&
-        key->key_code() == ui::VKEY_ESCAPE) {
-      escape_callback_.Run();
+        key->key_code() == ui::VKEY_ESCAPE && escape_callback_) {
+      std::move(escape_callback_).Run();
     }
   }
 
-  base::Closure escape_callback_;
+  base::OnceClosure escape_callback_;
   std::unique_ptr<views::EventMonitor> event_monitor_;
 
   DISALLOW_COPY_AND_ASSIGN(EscapeTracker);
@@ -427,10 +427,10 @@
       std::find(tabs.begin(), tabs.end(), source_tab) - tabs.begin();
 
   // Listen for Esc key presses.
-  escape_tracker_.reset(
-      new EscapeTracker(base::Bind(&TabDragController::EndDrag,
-                                   weak_factory_.GetWeakPtr(),
-                                   END_DRAG_CANCEL)));
+  escape_tracker_ = std::make_unique<EscapeTracker>(
+      base::BindOnce(&TabDragController::EndDrag, weak_factory_.GetWeakPtr(),
+                     END_DRAG_CANCEL),
+      source_tabstrip_->GetWidget()->GetNativeWindow());
 
   if (source_tab->width() > 0) {
     offset_to_width_ratio_ = static_cast<float>(
@@ -754,7 +754,7 @@
 
 #if defined(USE_AURA)
   // Only Aura windows are gesture consumers.
-  ui::GestureRecognizer::Get()->TransferEventsTo(
+  GetAttachedBrowserWidget()->GetGestureRecognizer()->TransferEventsTo(
       GetAttachedBrowserWidget()->GetNativeView(),
       target_tabstrip->GetWidget()->GetNativeView(),
       ui::GestureRecognizer::ShouldCancelTouches::DontCancel);
@@ -1230,9 +1230,9 @@
 
 #if defined(USE_AURA)
   // Only Aura windows are gesture consumers.
-  gfx::NativeView attached_native_view =
-      attached_tabstrip_->GetWidget()->GetNativeView();
-  ui::GestureRecognizer::Get()->TransferEventsTo(
+  views::Widget* attached_widget = attached_tabstrip_->GetWidget();
+  gfx::NativeView attached_native_view = attached_widget->GetNativeView();
+  attached_widget->GetGestureRecognizer()->TransferEventsTo(
       attached_native_view, dragged_widget->GetNativeView(),
       ui::GestureRecognizer::ShouldCancelTouches::DontCancel);
 #endif
@@ -2026,8 +2026,9 @@
     aura::Window* widget_window = widget->GetNativeWindow();
     DCHECK(widget_window->GetRootWindow());
     gfx::PointF touch_point_f;
-    bool got_touch_point = ui::GestureRecognizer::Get()->
-        GetLastTouchPointForTarget(widget_window, &touch_point_f);
+    bool got_touch_point =
+        widget->GetGestureRecognizer()->GetLastTouchPointForTarget(
+            widget_window, &touch_point_f);
     CHECK(got_touch_point);
     gfx::Point touch_point = gfx::ToFlooredPoint(touch_point_f);
     wm::ConvertPointToScreen(widget_window->GetRootWindow(), &touch_point);
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 7a5a367..84ed5f7 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -2180,7 +2180,7 @@
             this, gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)),
         this);
   }
-  mouse_watcher_->Start();
+  mouse_watcher_->Start(GetWidget()->GetNativeWindow());
 }
 
 void TabStrip::RemoveMessageLoopObserver() {
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index a8b12657..b303bb6 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -107,7 +107,7 @@
 #if defined(OS_CHROMEOS)
   // On platforms other than ChromeOS or when running under MASH, there is no
   // KeyboardController in the browser process.
-  if (features::IsAshInBrowserProcess()) {
+  if (!features::IsUsingWindowService()) {
     auto* keyboard_controller = keyboard::KeyboardController::Get();
     if (keyboard_controller->enabled() &&
         keyboard_controller->IsKeyboardVisible()) {
diff --git a/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc b/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc
index 266f1e9..d316999 100644
--- a/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc
+++ b/chrome/browser/ui/views/touch_events_interactive_uitest_win.cc
@@ -6,6 +6,8 @@
 #include "base/win/windows_version.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/view_event_test_base.h"
+#include "ui/aura/env.h"
+#include "ui/aura/test/env_test_helper.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/models/simple_menu_model.h"
@@ -132,15 +134,16 @@
   // ViewEventTestBase:
   void SetUp() override {
     touch_view_ = new views::View();
-    initial_gr_ = ui::GestureRecognizer::Get();
-    gesture_recognizer_ = std::make_unique<TestingGestureRecognizer>();
-    ui::SetGestureRecognizerForTesting(gesture_recognizer_.get());
     ViewEventTestBase::SetUp();
+    aura::test::EnvTestHelper().SetGestureRecognizer(
+        std::make_unique<TestingGestureRecognizer>());
+    gesture_recognizer_ = static_cast<TestingGestureRecognizer*>(
+        aura::Env::GetInstance()->gesture_recognizer());
   }
 
   void TearDown() override {
     touch_view_ = nullptr;
-    ui::SetGestureRecognizerForTesting(initial_gr_);
+    gesture_recognizer_ = nullptr;
     ViewEventTestBase::TearDown();
   }
 
@@ -185,7 +188,7 @@
 
  protected:
   views::View* touch_view_ = nullptr;
-  std::unique_ptr<TestingGestureRecognizer> gesture_recognizer_;
+  TestingGestureRecognizer* gesture_recognizer_ = nullptr;
   ui::GestureRecognizer* initial_gr_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(TouchEventsViewTest);
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index aecd32e..78a90905 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -382,6 +382,12 @@
   return true;
 }
 
+bool AuthenticatorTouchIdSheetModel::IsBackButtonVisible() const {
+  // Clicking back would not dismiss the native Touch ID dialog, which would be
+  // confusing. The user can cancel the native dialog to dismiss it.
+  return false;
+}
+
 gfx::ImageSkia* AuthenticatorTouchIdSheetModel::GetStepIllustration() const {
 #if defined(OS_MACOSX)
   return GetImage(IDR_WEBAUTHN_ILLUSTRATION_TOUCHID_1X);
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 050eef76..315b103 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -225,6 +225,7 @@
  private:
   // AuthenticatorSheetModelBase:
   bool IsActivityIndicatorVisible() const override;
+  bool IsBackButtonVisible() const override;
   gfx::ImageSkia* GetStepIllustration() const override;
   base::string16 GetStepTitle() const override;
   base::string16 GetStepDescription() const override;
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index b02061e..ca7ce752 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -6,13 +6,10 @@
 
 #include <memory>
 
-#include "ash/public/cpp/shell_window_ids.h"
 #include "base/bind.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/get_more_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/ready_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/third_party_screen_handler.h"
@@ -22,7 +19,6 @@
 #include "chrome/grit/browser_resources.h"
 #include "components/arc/arc_prefs.h"
 #include "components/prefs/pref_service.h"
-#include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 
@@ -112,13 +108,7 @@
     ash::mojom::AssistantSetup::StartAssistantOptInFlowCallback callback) {
   DCHECK(!is_active);
   AssistantOptInDialog* dialog = new AssistantOptInDialog(std::move(callback));
-
-  int container_id = session_manager::SessionManager::Get()->session_state() ==
-                             session_manager::SessionState::ACTIVE
-                         ? ash::kShellWindowId_AlwaysOnTopContainer
-                         : ash::kShellWindowId_LockSystemModalContainer;
-  chrome::ShowWebDialogInContainer(
-      container_id, ProfileManager::GetActiveUserProfile(), dialog, true);
+  dialog->ShowSystemDialog(true);
 }
 
 // static
@@ -130,9 +120,7 @@
     ash::mojom::AssistantSetup::StartAssistantOptInFlowCallback callback)
     : SystemWebDialogDelegate(GURL(chrome::kChromeUIAssistantOptInURL),
                               base::string16()),
-      callback_(std::move(callback)),
-      modal_type_(StartupUtils::IsOobeCompleted() ? ui::MODAL_TYPE_SYSTEM
-                                                  : ui::MODAL_TYPE_WINDOW) {
+      callback_(std::move(callback)) {
   DCHECK(!is_active);
   is_active = true;
 }
@@ -141,10 +129,6 @@
   is_active = false;
 }
 
-ui::ModalType AssistantOptInDialog::GetDialogModalType() const {
-  return modal_type_;
-}
-
 void AssistantOptInDialog::GetDialogSize(gfx::Size* size) const {
   size->SetSize(kAssistantOptInDialogWidth, kAssistantOptInDialogHeight);
 }
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
index 76ccf7b2..4174b73 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h
@@ -56,7 +56,6 @@
   ~AssistantOptInDialog() override;
 
   // ui::WebDialogDelegate
-  ui::ModalType GetDialogModalType() const override;
   void GetDialogSize(gfx::Size* size) const override;
   std::string GetDialogArgs() const override;
   bool ShouldShowDialogTitle() const override;
@@ -66,9 +65,6 @@
   // Callback to run if the flow is completed.
   ash::mojom::AssistantSetup::StartAssistantOptInFlowCallback callback_;
 
-  // Type of modality applied to the dialog window.
-  ui::ModalType modal_type_;
-
   DISALLOW_COPY_AND_ASSIGN(AssistantOptInDialog);
 };
 
diff --git a/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc b/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc
index de23739..f8357ae 100644
--- a/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc
+++ b/chrome/browser/unified_consent/chrome_unified_consent_service_client.cc
@@ -48,8 +48,10 @@
       enabled = pref_service_->GetBoolean(prefs::kAlternateErrorPagesEnabled);
       break;
     case Service::kMetricsReporting:
-      enabled =
-          ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
+      // Uploads are disabled for non-official builds, but UnifiedConsentService
+      // only cares whether the user has manually disabled metrics reporting.
+      enabled = g_browser_process->local_state()->GetBoolean(
+          metrics::prefs::kMetricsReportingEnabled);
       break;
     case Service::kNetworkPrediction:
       enabled = pref_service_->GetInteger(prefs::kNetworkPredictionOptions) ==
diff --git a/chrome/browser/vr/service/xr_device_impl.cc b/chrome/browser/vr/service/xr_device_impl.cc
index 681b68a4..c097e1aca 100644
--- a/chrome/browser/vr/service/xr_device_impl.cc
+++ b/chrome/browser/vr/service/xr_device_impl.cc
@@ -194,7 +194,7 @@
   client_ = std::move(client);
   BrowserXRRuntime* immersive_runtime =
       XRRuntimeManager::GetInstance()->GetImmersiveRuntime();
-  if (immersive_runtime && client) {
+  if (immersive_runtime && client_) {
     immersive_runtime->UpdateListeningForActivate(this);
   }
 }
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
index 1ae1740d..06fca75 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
@@ -19,6 +19,10 @@
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#endif
+
 namespace {
 
 constexpr char kWebAppManifestUrl[] = "web_app_manifest_url";
@@ -81,6 +85,34 @@
   return app_infos;
 }
 
+base::FilePath DetermineScanDir(Profile* profile) {
+  base::FilePath dir;
+#if defined(OS_CHROMEOS)
+  // As of mid 2018, only Chrome OS has default/external web apps, and
+  // chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS is only defined for OS_LINUX,
+  // which includes OS_CHROMEOS.
+
+  if (chromeos::ProfileHelper::IsPrimaryProfile(profile)) {
+    // For manual testing, you can change s/STANDALONE/USER/, as writing to
+    // "$HOME/.config/chromium/test-user/.config/chromium/External Extensions"
+    // does not require root ACLs, unlike "/usr/share/chromium/extensions".
+    //
+    // TODO(nigeltao): do we want to append a sub-directory name, analogous to
+    // the "arc" in "/usr/share/chromium/extensions/arc" as per
+    // chrome/browser/ui/app_list/arc/arc_default_app_list.cc? Or should we not
+    // sort "system apps" into directories based on their platform (e.g. ARC,
+    // PWA, etc.), and instead examine the JSON contents (e.g. an "activity"
+    // key means ARC, "web_app_start_url" key means PWA, etc.)?
+    if (!base::PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
+                                &dir)) {
+      LOG(ERROR) << "ScanForExternalWebApps: base::PathService::Get failed";
+    }
+  }
+
+#endif
+  return dir;
+}
+
 }  // namespace
 
 namespace web_app {
@@ -90,49 +122,28 @@
   return ScanDir(dir);
 }
 
-void ScanForExternalWebApps(ScanForExternalWebAppsCallback callback) {
+void ScanForExternalWebApps(Profile* profile,
+                            ScanForExternalWebAppsCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-#if !defined(OS_CHROMEOS)
-  // As of mid 2018, only Chrome OS has default/external web apps, and
-  // chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS is only defined for OS_LINUX,
-  // which includes OS_CHROMEOS.
-  //
-  // In the future we may have default/external web apps for other platforms,
-  // but for now run the callback with an empty vector.
-  std::move(callback).Run(std::vector<web_app::PendingAppManager::AppInfo>());
-#else
-  // For manual testing, it can be useful to s/STANDALONE/USER/, as writing to
-  // "$HOME/.config/chromium/test-user/.config/chromium/External Extensions"
-  // does not require root ACLs, unlike "/usr/share/chromium/extensions".
-  //
-  // TODO(nigeltao): do we want to append a sub-directory name, analogous to
-  // the "arc" in "/usr/share/chromium/extensions/arc" as per
-  // chrome/browser/ui/app_list/arc/arc_default_app_list.cc? Or should we not
-  // sort "system apps" into directories based on their platform (e.g. ARC,
-  // PWA, etc.), and instead examine the JSON contents (e.g. an "activity"
-  // key means ARC, "web_app_start_url" key means PWA, etc.)?
-  base::FilePath dir;
-  if (!base::PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
-                              &dir)) {
-    LOG(ERROR) << "ScanForExternalWebApps: base::PathService::Get failed";
-    return;
+  base::FilePath dir = DetermineScanDir(profile);
+  if (dir.empty()) {
+    std::move(callback).Run(std::vector<web_app::PendingAppManager::AppInfo>());
+  } else {
+    // Do a two-part callback dance, across different TaskRunners.
+    //
+    // 1. Schedule ScanDir to happen on a background thread, so that we don't
+    // block the UI thread. When that's done,
+    // base::PostTaskWithTraitsAndReplyWithResult will bounce us back to the
+    // originating thread (the UI thread).
+    //
+    // 2. In |callback|, forward the vector of AppInfo's on to the
+    // pending_app_manager_, which can only be called on the UI thread.
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE,
+        {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(&ScanDir, dir), std::move(callback));
   }
-
-  // Do a two-part callback dance, across different TaskRunners.
-  //
-  // 1. Schedule ScanDir to happen on a background thread, so that we don't
-  // block the UI thread. When that's done
-  // , base::PostTaskWithTraitsAndReplyWithResult will bounce us back to the
-  // originating thread (the UI thread).
-  //
-  // 2. In |callback|, forward the vector of AppInfo's on to the
-  // pending_app_manager_, which can only be called on the UI thread.
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(&ScanDir, dir), std::move(callback));
-#endif
 }
 
 }  //  namespace web_app
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps.h b/chrome/browser/web_applications/bookmark_apps/external_web_apps.h
index df350ea..1cd1598 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps.h
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps.h
@@ -11,12 +11,15 @@
 #include "base/files/file_path.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 
+class Profile;
+
 namespace web_app {
 
 using ScanForExternalWebAppsCallback =
     base::OnceCallback<void(std::vector<web_app::PendingAppManager::AppInfo>)>;
 
-void ScanForExternalWebApps(ScanForExternalWebAppsCallback callback);
+void ScanForExternalWebApps(Profile* profile,
+                            ScanForExternalWebAppsCallback callback);
 
 // Scans the given directory (non-recursively) for *.json files that define
 // "external web apps", the Web App analogs of "external extensions", described
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
index d3f5d99..f6961d4f 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/web_contents.h"
@@ -18,6 +19,8 @@
 
 namespace {
 
+const int kSecondsToWaitForWebContentsLoad = 30;
+
 std::unique_ptr<content::WebContents> WebContentsCreateWrapper(
     Profile* profile) {
   return content::WebContents::Create(
@@ -43,7 +46,8 @@
 PendingBookmarkAppManager::PendingBookmarkAppManager(Profile* profile)
     : profile_(profile),
       web_contents_factory_(base::BindRepeating(&WebContentsCreateWrapper)),
-      task_factory_(base::BindRepeating(&InstallationTaskCreateWrapper)) {}
+      task_factory_(base::BindRepeating(&InstallationTaskCreateWrapper)),
+      timer_(std::make_unique<base::OneShotTimer>()) {}
 
 PendingBookmarkAppManager::~PendingBookmarkAppManager() = default;
 
@@ -91,6 +95,11 @@
   task_factory_ = std::move(task_factory);
 }
 
+void PendingBookmarkAppManager::SetTimerForTesting(
+    std::unique_ptr<base::OneShotTimer> timer) {
+  timer_ = std::move(timer);
+}
+
 void PendingBookmarkAppManager::MaybeStartNextInstallation() {
   if (current_installation_)
     return;
@@ -110,6 +119,10 @@
       current_installation_->info.url);
   load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
   web_contents_->GetController().LoadURLWithParams(load_params);
+  timer_->Start(
+      FROM_HERE, base::TimeDelta::FromSeconds(kSecondsToWaitForWebContentsLoad),
+      base::BindOnce(&PendingBookmarkAppManager::OnWebContentsLoadTimedOut,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void PendingBookmarkAppManager::CreateWebContentsIfNecessary() {
@@ -125,6 +138,12 @@
   CurrentInstallationFinished(result.app_id);
 }
 
+void PendingBookmarkAppManager::OnWebContentsLoadTimedOut() {
+  web_contents_->Stop();
+  Observe(nullptr);
+  CurrentInstallationFinished(std::string());
+}
+
 void PendingBookmarkAppManager::CurrentInstallationFinished(
     const std::string& app_id) {
   // Post a task to avoid reentrancy issues e.g. adding a WebContentsObserver
@@ -144,6 +163,7 @@
 void PendingBookmarkAppManager::DidFinishLoad(
     content::RenderFrameHost* render_frame_host,
     const GURL& validated_url) {
+  timer_->Stop();
   if (web_contents_->GetMainFrame() != render_frame_host) {
     return;
   }
@@ -169,6 +189,7 @@
     const GURL& validated_url,
     int error_code,
     const base::string16& error_description) {
+  timer_->Stop();
   if (web_contents_->GetMainFrame() != render_frame_host) {
     return;
   }
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index 5cb7fd4..d52a857 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_installation_task.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -52,6 +53,8 @@
   void SetFactoriesForTesting(WebContentsFactory web_contents_factory,
                               TaskFactory task_factory);
 
+  void SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer);
+
  private:
   struct Installation;
 
@@ -61,6 +64,8 @@
 
   void OnInstalled(BookmarkAppInstallationTask::Result result);
 
+  void OnWebContentsLoadTimedOut();
+
   void CurrentInstallationFinished(const std::string& app_id);
 
   // WebContentsObserver
@@ -77,6 +82,7 @@
   TaskFactory task_factory_;
 
   std::unique_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<base::OneShotTimer> timer_;
 
   std::unique_ptr<Installation> current_installation_;
   std::unique_ptr<BookmarkAppInstallationTask> current_installation_task_;
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
index 21d4a64..92098340 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/test/bind_test_util.h"
+#include "base/timer/mock_timer.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/extensions/bookmark_app_installation_task.h"
@@ -554,4 +555,83 @@
   EXPECT_EQ(GURL(kBarWebAppUrl), install_callback_url());
 }
 
+TEST_F(PendingBookmarkAppManagerTest, WebContentsLoadTimedOut) {
+  auto pending_app_manager = GetPendingBookmarkAppManagerWithTestFactories();
+  auto timer_to_pass = std::make_unique<base::MockOneShotTimer>();
+  auto* timer = timer_to_pass.get();
+
+  pending_app_manager->SetTimerForTesting(std::move(timer_to_pass));
+
+  // Queue through Install.
+  pending_app_manager->Install(
+      GetQuxAppInfo(),
+      base::BindOnce(&PendingBookmarkAppManagerTest::InstallCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer->IsRunning());
+
+  // Verify that the timer is stopped after a successful load.
+  SuccessfullyLoad(GURL(kQuxWebAppUrl));
+  EXPECT_FALSE(timer->IsRunning());
+  EXPECT_TRUE(install_succeeded());
+  EXPECT_EQ(GURL(kQuxWebAppUrl), install_callback_url());
+  ResetResults();
+
+  // Queue through Install.
+  pending_app_manager->Install(
+      GetQuxAppInfo(),
+      base::BindOnce(&PendingBookmarkAppManagerTest::InstallCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer->IsRunning());
+
+  // Fire the timer to simulate a failed load.
+  timer->Fire();
+  EXPECT_FALSE(install_succeeded());
+  EXPECT_EQ(GURL(kQuxWebAppUrl), install_callback_url());
+  ResetResults();
+
+  // Queue through InstallApps.
+  std::vector<web_app::PendingAppManager::AppInfo> apps_to_install;
+  apps_to_install.push_back(GetFooAppInfo());
+  apps_to_install.push_back(GetBarAppInfo());
+
+  pending_app_manager->InstallApps(
+      std::move(apps_to_install),
+      base::BindRepeating(&PendingBookmarkAppManagerTest::InstallCallback,
+                          base::Unretained(this)));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer->IsRunning());
+
+  // Fire the timer to simulate a failed load.
+  timer->Fire();
+  EXPECT_FALSE(install_succeeded());
+  EXPECT_EQ(GURL(kFooWebAppUrl), install_callback_url());
+  ResetResults();
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer->IsRunning());
+
+  // Fire the timer to simulate a failed load.
+  timer->Fire();
+  EXPECT_FALSE(install_succeeded());
+  EXPECT_EQ(GURL(kBarWebAppUrl), install_callback_url());
+  ResetResults();
+
+  // Ensure a successful load after a timer fire works.
+  pending_app_manager->Install(
+      GetBarAppInfo(),
+      base::BindOnce(&PendingBookmarkAppManagerTest::InstallCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(timer->IsRunning());
+
+  // Verify that the timer is stopped after a successful load.
+  SuccessfullyLoad(GURL(kBarWebAppUrl));
+  EXPECT_FALSE(timer->IsRunning());
+  EXPECT_TRUE(install_succeeded());
+  EXPECT_EQ(GURL(kBarWebAppUrl), install_callback_url());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index fbbfff1c..596e7d83 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -27,8 +27,8 @@
           std::make_unique<WebAppPolicyManager>(profile->GetPrefs(),
                                                 pending_app_manager_.get())) {
   web_app::ScanForExternalWebApps(
-      base::BindOnce(&WebAppProvider::OnScanForExternalWebApps,
-                     weak_ptr_factory_.GetWeakPtr()));
+      profile, base::BindOnce(&WebAppProvider::OnScanForExternalWebApps,
+                              weak_ptr_factory_.GetWeakPtr()));
 }
 
 WebAppProvider::~WebAppProvider() = default;
@@ -39,6 +39,13 @@
   WebAppPolicyManager::RegisterProfilePrefs(registry);
 }
 
+void WebAppProvider::Shutdown() {
+  // PendingAppManager is used by WebAppPolicyManager and therefore should be
+  // deleted after it.
+  web_app_policy_manager_.reset();
+  pending_app_manager_.reset();
+}
+
 void WebAppProvider::OnScanForExternalWebApps(
     std::vector<web_app::PendingAppManager::AppInfo> app_infos) {
   pending_app_manager_->InstallApps(std::move(app_infos), base::DoNothing());
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index 0f1c1d5a..801c0b146 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -41,6 +41,9 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  // KeyedService
+  void Shutdown() override;
+
  private:
   void OnScanForExternalWebApps(
       std::vector<web_app::PendingAppManager::AppInfo>);
diff --git a/chrome/chrome_cleaner/interfaces/BUILD.gn b/chrome/chrome_cleaner/interfaces/BUILD.gn
new file mode 100644
index 0000000..7f48e4ca
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("engine_sandbox_interface") {
+  sources = [
+    "cleaner_engine_requests.mojom",
+    "engine_file_requests.mojom",
+    "engine_requests.mojom",
+    "engine_sandbox.mojom",
+    "pup.mojom",
+    "string16_embedded_nulls.mojom",
+    "windows_handle.mojom",
+  ]
+  deps = [
+    "//components/chrome_cleaner/public/interfaces:interfaces",
+    "//mojo/public/mojom/base",
+  ]
+}
diff --git a/chrome/chrome_cleaner/interfaces/OWNERS b/chrome/chrome_cleaner/interfaces/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chrome/chrome_cleaner/interfaces/cleaner_engine_requests.mojom b/chrome/chrome_cleaner/interfaces/cleaner_engine_requests.mojom
new file mode 100644
index 0000000..7eb77f1
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/cleaner_engine_requests.mojom
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+import "chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom";
+import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+import "mojo/public/mojom/base/string16.mojom";
+
+// Passes requests that can mutate the system from the low-privilege sandbox
+// target process to the high-privilege broker process. It is implemented in
+// CleanerEngineRequestsImpl in engines/broker.
+//
+// This interface is only used when in cleaning mode, in which case the broker
+// process runs with administrator privileges, so the parameters of each method
+// must be carefully validated to ensure that the requests are safe.
+interface CleanerEngineRequests {
+  // Attempts to deletes the given file, applying some basic checks to ensure
+  // the file is safe to delete.
+  SandboxDeleteFile(FilePath file_name) => (bool result);
+
+  // Schedules the given file for post-reboot removal, applying some basic
+  // checks to ensure the file is safe to delete.
+  SandboxDeleteFilePostReboot(FilePath file_name) => (bool result);
+
+  // Deletes the given registry key. |key| may contain null characters.
+  SandboxNtDeleteRegistryKey(String16EmbeddedNulls key) => (bool result);
+
+  // Deletes the given value for the given registry key. |key| and |key_name|
+  // may contain null characters.
+  SandboxNtDeleteRegistryValue(String16EmbeddedNulls key,
+                               String16EmbeddedNulls value_name)
+    => (bool result);
+
+  // Updates the value of the given key's value to |new_value|.
+  // |new_value| must be a subset of the existing value. This is intended to be
+  // used to delete parts of a value, not to set a new value.
+  SandboxNtChangeRegistryValue(String16EmbeddedNulls key,
+                               String16EmbeddedNulls value_name,
+                               String16EmbeddedNulls new_value)
+    => (bool result);
+
+  // Deletes the given service.
+  SandboxDeleteService(mojo_base.mojom.String16 name) => (bool result);
+
+  // Deletes the given task.
+  SandboxDeleteTask(mojo_base.mojom.String16 name) => (bool result);
+
+  // Terminates the given process.
+  // The broker process can't be terminated.
+  SandboxTerminateProcess(uint32 process_id) => (bool result);
+};
diff --git a/chrome/chrome_cleaner/interfaces/engine_file_requests.mojom b/chrome/chrome_cleaner/interfaces/engine_file_requests.mojom
new file mode 100644
index 0000000..beba962e
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/engine_file_requests.mojom
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+
+// Handles returned by FindFirstFile aren't real handles, so we can't pass them
+// through mojo as handles, since they can't be duplicated.
+struct FindHandle {
+  int64 find_handle;
+};
+
+struct FindFileData {
+  array<uint8> data;
+};
+
+// Passes file handling requests from the low-privilege sandbox target process
+// to the high-privilege broker process. It is implemented in
+// EngineFileRequestsImpl in engines/broker.
+//
+// This interface is used in scanning and cleaning mode, and when initializing
+// the engine (which may need to load auxiliary file resources.)
+interface EngineFileRequests {
+  // Calls ::FindFirstFile for the given path, returning the results from
+  // ::FindFirstFile.
+  SandboxFindFirstFile(FilePath file_name) =>
+    (uint32 result, FindFileData win32_find_data, FindHandle find_handle);
+
+  // Calls ::FindNextFile for the given handle, returning the results from
+  // ::FindNextFile.
+  SandboxFindNextFile(FindHandle find_handle) =>
+    (uint32 result, FindFileData win32_find_data);
+
+  // Calls ::FindClose on the given handle, returning the results from
+  // ::FindClose.
+  SandboxFindClose(FindHandle find_handle) => (uint32 result);
+
+  // Returns a read-only file handle for the given file. The only acceptable
+  // values for |dwFlagsAndAttributes| are FILE_FLAG_NO_BUFFERING,
+  // FILE_FLAG_SEQUENTIAL_SCAN, FILE_FLAG_RANDOM_ACCESS, and
+  // FILE_FLAG_OPEN_REPARSE_POINT.
+  SandboxOpenReadOnlyFile(FilePath file_name, uint32 dwFlagsAndAttributes) =>
+    (handle result);
+};
diff --git a/chrome/chrome_cleaner/interfaces/engine_requests.mojom b/chrome/chrome_cleaner/interfaces/engine_requests.mojom
new file mode 100644
index 0000000..a816495
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/engine_requests.mojom
@@ -0,0 +1,90 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+module chrome_cleaner.mojom;
+
+import "chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom";
+import "chrome/chrome_cleaner/interfaces/windows_handle.mojom";
+import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+import "mojo/public/mojom/base/process_id.mojom";
+import "mojo/public/mojom/base/string16.mojom";
+
+enum KnownFolder {
+  kWindows = 0,
+  kProgramFiles = 1,
+  kProgramFilesX86 = 2,
+  kAppData = 3,
+};
+
+struct StringSid {
+  mojo_base.mojom.String16 value;
+};
+
+struct ScheduledTaskAction {
+  FilePath path;
+  FilePath working_dir;
+  mojo_base.mojom.String16 arguments;
+};
+
+struct ScheduledTask {
+  mojo_base.mojom.String16 name;
+  mojo_base.mojom.String16 description;
+  array<ScheduledTaskAction> actions;
+};
+
+struct UserInformation {
+  mojo_base.mojom.String16 name;
+  mojo_base.mojom.String16 domain;
+  // User account type (SID_NAME_USE). See
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa379601(v=vs.85).aspx
+  uint32 account_type;
+};
+
+// Passes requests that do not mutate the system from the low-privilege sandbox
+// target process to the high-privilege broker process. It is implemented in
+// EngineRequestsImpl in engines/broker.
+//
+// This interface is used in scanning and cleaning mode.
+interface EngineRequests {
+  // Converts the given KnownFolder Id to its folder path.
+  SandboxGetKnownFolderPath(KnownFolder folder_id) =>
+    (bool result, FilePath folder_path);
+
+  // Gets all of the processes currently running on the machine.
+  SandboxGetProcesses() =>
+      (bool result, array<mojo_base.mojom.ProcessId> processes);
+
+  // Gets all the tasks on the system.
+  SandboxGetTasks() => (bool result, array<ScheduledTask> tasks);
+
+  // Returns the path to the executable of the given process.
+  SandboxGetProcessImagePath(mojo_base.mojom.ProcessId pid) =>
+    (bool result, FilePath image_path);
+
+  // Gets all of the modules loaded into memory for the given process.
+  SandboxGetLoadedModules(mojo_base.mojom.ProcessId pid) =>
+    (bool result, array<mojo_base.mojom.String16> modules);
+
+  // Gets the command line for the given process.
+  SandboxGetProcessCommandLine(mojo_base.mojom.ProcessId pid) =>
+    (bool result, mojo_base.mojom.String16 command_line);
+
+  // Gets the given UserInformation values for |sid|.
+  SandboxGetUserInfoFromSID(StringSid sid) => (bool result,
+                                               UserInformation user_info);
+
+  // Gets a read-only registry key handle to the given key.
+  // |dw_access| may specify KEY_WOW64_32KEY or KEY_WOW64_64KEY. |root_key| must
+  // be non-null, and |sub_key| can't have any null characters.
+  SandboxOpenReadOnlyRegistry(WindowsHandle root_key,
+          mojo_base.mojom.String16 sub_key, uint32 dw_access) =>
+    (uint32 result, WindowsHandle reg_handle);
+
+  // Gets a read-only registry key handle to the given key.
+  // |dw_access| may not specify KEY_WOW64_32KEY or KEY_WOW64_64KEY.
+  // ||root_key| may be null, and |sub_key| may have null characters.
+  SandboxNtOpenReadOnlyRegistry(WindowsHandle root_key,
+                                String16EmbeddedNulls sub_key,
+                                uint32 dw_access) =>
+    (uint32 result, WindowsHandle reg_handle);
+};
diff --git a/chrome/chrome_cleaner/interfaces/engine_sandbox.mojom b/chrome/chrome_cleaner/interfaces/engine_sandbox.mojom
new file mode 100644
index 0000000..78f33be
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/engine_sandbox.mojom
@@ -0,0 +1,79 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+import "chrome/chrome_cleaner/interfaces/cleaner_engine_requests.mojom";
+import "chrome/chrome_cleaner/interfaces/engine_requests.mojom";
+import "chrome/chrome_cleaner/interfaces/engine_file_requests.mojom";
+import "chrome/chrome_cleaner/interfaces/pup.mojom";
+import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+
+// All result_code parameters and return values in these interfaces correspond
+// to ESETResultCode values for the ESET engine, and to
+// EngineOperationStatus::ResultCode values the Urza engine.
+
+// This interface passes scan results back from the target process to the
+// broker process.
+interface EngineScanResults {
+  // Called zero or more times with details of UwS.
+  FoundUwS(uint32 pup_id, PUP pup);
+
+  // Called exactly once, after any calls to FoundUwS.
+  Done(uint32 result_code);
+};
+
+// This interface passes cleanup results back from the target process to the
+// broker process.
+interface EngineCleanupResults {
+  // Called once the cleaner has finished cleaning.
+  Done(uint32 result_code);
+};
+
+// This interface connects the sandbox broker process to the sandbox target
+// process, which implements the interface using functions declared in
+// third_party/eset_lib/src/api/eset_cleaner.h.
+interface EngineCommands {
+  // Runs the engine's initialization routine.
+  Initialize(associated EngineFileRequests file_requests,
+             FilePath log_directory) => (uint32 result_code);
+
+  // Starts scanning the user's system.
+  // |enabled_uws| contains a list of UwS IDs to scan for.
+  // |enabled_trace_locations| is a list of trace locations, to which scanning
+  // should be limited.
+  // |include_details| is true if the results should include full details of
+  // each UwS found, false if the results should include only the ID.
+  // |results| is an interface which will be used to return results:
+  // FoundUwS will be called 0 or more times with details of the UwS found,
+  // followed by exactly one call to Done.
+  //
+  // If the scan request returns an error immediately, that error is returned
+  // and |results| is not used. Otherwise a success result code is returned and
+  // any further errors will be reported by calling Done on the |results|
+  // interface.
+  StartScan(array<uint32> enabled_uws,
+            array<TraceLocation> enabled_trace_locations,
+            bool include_details,
+            associated EngineFileRequests file_requests,
+            associated EngineRequests sandboxed_engine_requests,
+            associated EngineScanResults results) => (uint32 result_code);
+
+  // Starts cleaning up the user's system. |enabled_uws| contains a list of UwS
+  // IDs to cleanup.
+  //
+  // If the cleanup request returns an error immediately, that error is returned
+  // and |results| is not used. Otherwise a success result code is returned and
+  // any further errors will be reported by calling Done on the |results|
+  // interface.
+  StartCleanup(array<uint32> enabled_uws,
+               associated EngineFileRequests file_requests,
+               associated EngineRequests sandboxed_engine_requests,
+               associated CleanerEngineRequests
+                 sandboxed_cleaner_engine_requests,
+               associated EngineCleanupResults results) => (uint32 result_code);
+
+  // Runs the engine's finalization routine.
+  Finalize() => (uint32 result_code);
+};
diff --git a/chrome/chrome_cleaner/interfaces/pup.mojom b/chrome/chrome_cleaner/interfaces/pup.mojom
new file mode 100644
index 0000000..3b871c8
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/pup.mojom
@@ -0,0 +1,61 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+import "chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom";
+import "chrome/chrome_cleaner/interfaces/windows_handle.mojom";
+import "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom";
+import "mojo/public/mojom/base/string16.mojom";
+
+// Source:
+//   https://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx
+enum Wow64Access {
+  kNone = 0,
+  // KEY_WOW64_64KEY
+  k64Key = 0x0100,
+  // KEY_WOW64_32KEY
+  k32Key = 0x0200,
+};
+
+// Typemapped to chrome_cleaner::RegKeyPath.
+struct RegKeyPath {
+  WindowsHandle rootkey;
+  // This is only sent by URZA, which currently doesn't support registry paths
+  // with embedded nulls.
+  mojo_base.mojom.String16 subkey;
+  Wow64Access wow64access;
+};
+
+// Used for reporting detected registry footprints.
+// Typemapped to chrome_cleaner::PUPData::RegistryFootprint.
+struct RegistryFootprint {
+  RegKeyPath key_path;
+  String16EmbeddedNulls value_name;
+  String16EmbeddedNulls value_substring;
+  // An enumerator of chrome_cleaner::RegistryMatchRule.
+  uint32 rule;
+};
+
+// Typemapped to chrome_cleaner::UwS::TraceLocation enumeration from
+// chrome_cleaner/logging/proto/shared_data.proto.
+// The struct is used here as a work-around to make Mojo check passed values
+// without having to duplicate the enum definition.
+struct TraceLocation {
+  int32 value;
+};
+
+// Typemapped to chrome_cleaner::PUPData::FileInfo.
+struct FileInfo {
+  array<TraceLocation> found_in;
+};
+
+// Partially typemapped to chrome_cleaner::PUPData::PUP.
+// UwS signatures are not included.
+struct PUP {
+  array<FilePath> expanded_disk_footprints;
+  array<RegistryFootprint> expanded_registry_footprints;
+  array<mojo_base.mojom.String16> expanded_scheduled_tasks;
+  map<FilePath, FileInfo> disk_footprints_info;
+};
diff --git a/chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom b/chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom
new file mode 100644
index 0000000..a8aa080e
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/string16_embedded_nulls.mojom
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+// Typemapped to chrome_cleaner::String16EmbeddedNulls.
+//
+// Note: Mojo doesn't allow sending null arrays over the wire, and the strings
+//       represented by this type can be empty (without a null at the end).
+//       Because of that, represented as a union of either something that is
+//       always empty (NullValue) or a non-empty array of uint16.
+union String16EmbeddedNulls {
+  // The underlying string is a null array (not the same as an empty string,
+  // which has at least one character '\0').
+  NullValue? null_value;
+
+  // The underlying string is either a null-terminated empty string (size is 1),
+  // or a non-empty string that can be either null-terminated or not.
+  array<uint16> value;
+};
+
+struct NullValue {
+};
diff --git a/chrome/chrome_cleaner/interfaces/windows_handle.mojom b/chrome/chrome_cleaner/interfaces/windows_handle.mojom
new file mode 100644
index 0000000..5fb1f61
--- /dev/null
+++ b/chrome/chrome_cleaner/interfaces/windows_handle.mojom
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chrome_cleaner.mojom;
+
+enum SpecialWindowsHandle {
+  NULL_HANDLE,
+  INVALID_HANDLE,
+  CLASSES_ROOT,
+  CURRENT_CONFIG,
+  CURRENT_USER,
+  LOCAL_MACHINE,
+  USERS,
+};
+
+// Mojo's |handle| type passes handles with DUPLICATE_CLOSE_SOURCE. The special
+// handles above can't be closed, so they can't be passed as |handle|. Use a
+// wrapper that puts these in |special_handle| and plain handles in
+// |raw_handle|. Typemapped to HANDLE.
+union WindowsHandle {
+  handle raw_handle;
+  SpecialWindowsHandle special_handle;
+};
+
+interface TestWindowsHandle {
+  EchoHandle(WindowsHandle in_WindowsHandle) =>
+    (WindowsHandle out_WindowsHandle);
+
+  EchoRawHandle(handle in_handle) => (handle out_handle);
+};
diff --git a/chrome/chrome_cleaner/ipc/sandbox.cc b/chrome/chrome_cleaner/ipc/sandbox.cc
index 7c61918..0477a232 100644
--- a/chrome/chrome_cleaner/ipc/sandbox.cc
+++ b/chrome/chrome_cleaner/ipc/sandbox.cc
@@ -295,17 +295,28 @@
     return RESULT_CODE_FAILED_TO_START_SANDBOX_PROCESS;
   }
 
-  // Wait for the sandboxed process to signal it is ready, checking every now
-  // and then to see if the process crashed before it could complete it's setup.
-  int exit_code = -1;
-  while (!init_done_event->TimedWait(base::TimeDelta::FromSeconds(1))) {
-    if (process_handle.WaitForExitWithTimeout(base::TimeDelta(), &exit_code)) {
+  // Wait for the sandboxed process to signal it is ready, or for the process
+  // to exit, indicating a failure.
+  std::vector<HANDLE> wait_handles{init_done_event->handle(),
+                                   process_handle.Handle()};
+  DWORD wait_result = ::WaitForMultipleObjects(
+      wait_handles.size(), wait_handles.data(), /*bWaitAll=*/false, INFINITE);
+  // WAIT_OBJECT_0 is the first handle in the vector, so if we got any other
+  // result it is a failure.
+  if (wait_result != WAIT_OBJECT_0) {
+    if (wait_result == WAIT_OBJECT_0 + 1) {
+      DWORD exit_code = -1;
+      BOOL result = ::GetExitCodeProcess(process_handle.Handle(), &exit_code);
+      DCHECK(result);
       LOG(ERROR)
           << "Sandboxed process exited before signaling it was initialized, "
              "exit code: "
           << exit_code;
-      return RESULT_CODE_FAILED_TO_START_SANDBOX_PROCESS;
+    } else {
+      PLOG(ERROR) << "::WaitForMultipleObjects returned an unexpected error, "
+                  << wait_result;
     }
+    return RESULT_CODE_FAILED_TO_START_SANDBOX_PROCESS;
   }
 
   if (hooks) {
diff --git a/chrome/chrome_cleaner/ipc/sandbox_unittest.cc b/chrome/chrome_cleaner/ipc/sandbox_unittest.cc
index 4c3eca9..cee1bca 100644
--- a/chrome/chrome_cleaner/ipc/sandbox_unittest.cc
+++ b/chrome/chrome_cleaner/ipc/sandbox_unittest.cc
@@ -21,7 +21,6 @@
 #include "base/strings/string_util.h"
 #include "base/test/multiprocess_test.h"
 #include "base/win/scoped_handle.h"
-#include "base/win/windows_version.h"
 #include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
 #include "chrome/chrome_cleaner/logging/scoped_logging.h"
 #include "chrome/chrome_cleaner/os/disk_util.h"
@@ -162,13 +161,7 @@
 
 }  // namespace
 
-// Flaky; see http://crbug.com/874387
-TEST_F(SandboxTest, DISABLED_SpawnSandboxTarget) {
-  if (base::win::GetVersion() < base::win::VERSION_WIN8) {
-    // TODO(b/871924): This test is currently failing on win7. Fix and enable.
-    return;
-  }
-
+TEST_F(SandboxTest, SpawnSandboxTarget) {
   base::Process target_process;
   EXPECT_TRUE(SpawnMockSandboxProcess(&target_process));
   EXPECT_TRUE(target_process.IsValid());
diff --git a/chrome/chrome_cleaner/test/test_main.cc b/chrome/chrome_cleaner/test/test_main.cc
index 4286710..856c87a 100644
--- a/chrome/chrome_cleaner/test/test_main.cc
+++ b/chrome/chrome_cleaner/test/test_main.cc
@@ -9,6 +9,7 @@
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "base/win/scoped_com_initializer.h"
+#include "base/win/windows_version.h"
 #include "chrome/chrome_cleaner/crash/crash_client.h"
 #include "chrome/chrome_cleaner/ipc/sandbox.h"
 #include "chrome/chrome_cleaner/logging/scoped_logging.h"
@@ -95,12 +96,20 @@
         chrome_cleaner::SandboxType::kNonSandboxed);
   }
 
+  // Some tests spawn sandbox targets using job objects. Windows 7 doesn't
+  // support nested job objects, so don't use them in the test suite. Otherwise
+  // all sandbox tests will fail as they try to create a second job object.
+  bool use_job_objects = base::win::GetVersion() >= base::win::VERSION_WIN8;
+
   // Some tests will fail if two tests try to launch test_process.exe
   // simultaneously, so run the tests serially. This will still shard them and
   // distribute the shards to different swarming bots, but tests will run
   // serially on each bot.
-  const int result = base::LaunchUnitTestsSerially(
+  const int result = base::LaunchUnitTestsWithOptions(
       argc, argv,
+      /*parallel_jobs=*/1U,        // Like LaunchUnitTestsSerially
+      /*default_batch_limit=*/10,  // Like LaunchUnitTestsSerially
+      use_job_objects,
       base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
 
   if (!IsSandboxedProcess())
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index 867359dc..6f90411 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -14,14 +14,15 @@
 
 # TODO(devlin): Enforce visibility restrictions on more of these targets?
 
-json_schema_api("generated_api_bundles") {
-  sources = chrome_extensions_api_schema_sources
-  bundle = true
+generated_json_strings("generated_api_json_strings") {
+  sources = chrome_extensions_api_schema_sources +
+            chrome_extensions_api_uncompiled_sources
+
   configs = [ "//build/config:precompiled_headers" ]
   bundle_name = "Chrome"
   schema_include_rules = chrome_extensions_api_schema_include_rules
 
-  uncompiled_bundle_schema_sources = [
+  sources += [
     "app.json",
     "browser_action.json",
     "commands.json",
@@ -29,22 +30,20 @@
     "page_action.json",
     "privacy.json",
     "proxy.json",
-    "tts_engine.json",
     "tts.json",
+    "tts_engine.json",
     "webstore.json",
   ]
   if (is_chromeos) {
-    uncompiled_bundle_schema_sources += [ "file_browser_handler.json" ]
+    sources += [ "file_browser_handler.json" ]
   } else {
     # On ChromeOS, input_method_private is fully compiled (as part of
     # schema_files), and so gets added to the bundle already. On other
     # platforms, we still need it added to the bundle.
     # TODO(devlin): That's weird. Investigate.
-    uncompiled_bundle_schema_sources += [ "input_method_private.json" ]
+    sources += [ "input_method_private.json" ]
   }
 
-  uncompiled_sources = chrome_extensions_api_uncompiled_sources
-
   root_namespace = chrome_extensions_api_root_namespace
   deps = [
     "//extensions/common/api",
@@ -94,7 +93,7 @@
 
 group("api") {
   public_deps = [
-    ":generated_api_bundles",
+    ":generated_api_json_strings",
     ":generated_api_types",
   ]
 }
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index 4008541..0870e048 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -302,7 +302,7 @@
       light_suffix, elements_dir.c_str(), logo_suffix, light_suffix,
       elements_dir.c_str(), logo_suffix, light_suffix,
       use_light_assets ? L"dark" : L"light",
-      use_light_assets ? L"#FFFFFF" : L"#212121"));
+      use_light_assets ? L"#FFFFFF" : L"#5F6368"));
 
   return base::UTF16ToUTF8(manifest16);
 }
@@ -328,12 +328,7 @@
   DCHECK(base::PathExists(visual_elements_dir.Append(
       base::StringPrintf(L"Logo%ls.png", logo_suffix))));
 
-  // Check for light assets that require dark text.
-  base::string16 light_logo_file_name =
-      base::StringPrintf(L"Logo%lsLight.png", logo_suffix);
-  return base::PathExists(visual_elements_dir.Append(light_logo_file_name))
-             ? VEAssetType::kDarkAndLight
-             : VEAssetType::kDarkOnly;
+  return VEAssetType::kDarkOnly;
 }
 
 }  // namespace
diff --git a/chrome/installer/setup/install_unittest.cc b/chrome/installer/setup/install_unittest.cc
index 6ef0806..f9583a1 100644
--- a/chrome/installer/setup/install_unittest.cc
+++ b/chrome/installer/setup/install_unittest.cc
@@ -160,18 +160,18 @@
     "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogo.png'\r\n"
     "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogo.png'\r\n"
     "      ForegroundText='light'\r\n"
-    "      BackgroundColor='#212121'/>\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 constexpr char kExpectedPrimaryLightManifest[] =
     "<Application xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>\r\n"
     "  <VisualElements\r\n"
     "      ShowNameOnSquare150x150Logo='on'\r\n"
-    "      Square150x150Logo='0.0.0.0\\VisualElements\\LogoLight.png'\r\n"
-    "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoLight.png'\r\n"
-    "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoLight.png'\r\n"
-    "      ForegroundText='dark'\r\n"
-    "      BackgroundColor='#FFFFFF'/>\r\n"
+    "      Square150x150Logo='0.0.0.0\\VisualElements\\Logo.png'\r\n"
+    "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogo.png'\r\n"
+    "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogo.png'\r\n"
+    "      ForegroundText='light'\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 #if defined(GOOGLE_CHROME_BUILD)
@@ -183,20 +183,20 @@
     "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoBeta.png'\r\n"
     "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoBeta.png'\r\n"
     "      ForegroundText='light'\r\n"
-    "      BackgroundColor='#212121'/>\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 constexpr char kExpectedBetaLightManifest[] =
     "<Application xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>\r\n"
     "  <VisualElements\r\n"
     "      ShowNameOnSquare150x150Logo='on'\r\n"
-    "      Square150x150Logo='0.0.0.0\\VisualElements\\LogoBetaLight.png'\r\n"
+    "      Square150x150Logo='0.0.0.0\\VisualElements\\LogoBeta.png'\r\n"
     "      "
-    "Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoBetaLight.png'\r\n"
+    "Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoBeta.png'\r\n"
     "      "
-    "Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoBetaLight.png'\r\n"
-    "      ForegroundText='dark'\r\n"
-    "      BackgroundColor='#FFFFFF'/>\r\n"
+    "Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoBeta.png'\r\n"
+    "      ForegroundText='light'\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 constexpr char kExpectedDevManifest[] =
@@ -207,18 +207,18 @@
     "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoDev.png'\r\n"
     "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoDev.png'\r\n"
     "      ForegroundText='light'\r\n"
-    "      BackgroundColor='#212121'/>\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 constexpr char kExpectedDevLightManifest[] =
     "<Application xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>\r\n"
     "  <VisualElements\r\n"
     "      ShowNameOnSquare150x150Logo='on'\r\n"
-    "      Square150x150Logo='0.0.0.0\\VisualElements\\LogoDevLight.png'\r\n"
-    "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoDevLight.png'\r\n"
-    "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoDevLight.png'\r\n"
-    "      ForegroundText='dark'\r\n"
-    "      BackgroundColor='#FFFFFF'/>\r\n"
+    "      Square150x150Logo='0.0.0.0\\VisualElements\\LogoDev.png'\r\n"
+    "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoDev.png'\r\n"
+    "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoDev.png'\r\n"
+    "      ForegroundText='light'\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 constexpr char kExpectedCanaryManifest[] =
@@ -229,7 +229,7 @@
     "      Square70x70Logo='0.0.0.0\\VisualElements\\SmallLogoCanary.png'\r\n"
     "      Square44x44Logo='0.0.0.0\\VisualElements\\SmallLogoCanary.png'\r\n"
     "      ForegroundText='light'\r\n"
-    "      BackgroundColor='#212121'/>\r\n"
+    "      BackgroundColor='#5F6368'/>\r\n"
     "</Application>\r\n";
 
 INSTANTIATE_TEST_CASE_P(
@@ -536,24 +536,14 @@
   installer::UpdateVisualElementsManifest(test_dir_.GetPath(), version_,
                                           !supports_dark_text_);
 
-  // The file should have been modified only if the brand has light assets.
+  // The file should remain using the dark assets.
   ASSERT_TRUE(base::GetFileInfo(manifest_path_, &manifest_info_after));
-  if (has_light_assets_) {
-    EXPECT_NE(manifest_info_before.last_modified,
-              manifest_info_after.last_modified);
-  } else {
-    EXPECT_EQ(manifest_info_before.last_modified,
-              manifest_info_after.last_modified);
-  }
+  EXPECT_EQ(manifest_info_before.last_modified,
+            manifest_info_after.last_modified);
   ASSERT_TRUE(
       base::GetFileInfo(start_menu_shortcut_path_, &shortcut_info_after));
-  if (has_light_assets_) {
-    EXPECT_NE(shortcut_info_before.last_modified,
-              shortcut_info_after.last_modified);
-  } else {
-    EXPECT_EQ(shortcut_info_before.last_modified,
-              shortcut_info_after.last_modified);
-  }
+  EXPECT_EQ(shortcut_info_before.last_modified,
+            shortcut_info_after.last_modified);
 }
 
 TEST_F(InstallShortcutTest, CreateAllShortcuts) {
diff --git a/chrome/test/data/ad_tagging/ad_script.js b/chrome/test/data/ad_tagging/ad_script.js
index ed2812b..532c9ad 100644
--- a/chrome/test/data/ad_tagging/ad_script.js
+++ b/chrome/test/data/ad_tagging/ad_script.js
@@ -7,6 +7,10 @@
   document.body.appendChild(frame);
 }
 
+function windowOpenFromAdScript() {
+  window.open();
+}
+
 async function createDocWrittenAdFrame(name, base_url) {
   let doc_body = await fetch('frame_factory.html');
   let doc_text = await doc_body.text();
diff --git a/chrome/test/data/ad_tagging/create_frame.js b/chrome/test/data/ad_tagging/create_frame.js
index a1d7304..bba5be71 100644
--- a/chrome/test/data/ad_tagging/create_frame.js
+++ b/chrome/test/data/ad_tagging/create_frame.js
@@ -7,6 +7,10 @@
   document.body.appendChild(frame);
 }
 
+function windowOpenFromNonAdScript() {
+  window.open();
+}
+
 async function createDocWrittenFrame(name, base_url) {
   let doc_body = await fetch('frame_factory.html');
   let doc_text = await doc_body.text();
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index abae33d..ce0ea7e 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -70,6 +70,15 @@
       <div id="edit-bg" hidden>
         <button id="edit-bg-gear"></button>
       </div>
+
+      <div id="message-box-container" class="message-box-hide">
+        <div id="message-box">
+          <div id="message-box-icon"></div>
+          <div id="message-box-message"></div>
+          <div id="message-box-link" class="ripple"></div>
+        </div>
+      </div>
+      <div id="custom-bg-attr"></div>
     </div>
 
     <dialog div id="edit-bg-dialog">
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index b2a8c63..cb17150 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <stddef.h>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/test/test_timeouts.h"
@@ -32,13 +33,19 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/javascript_test_observer.h"
+#include "content/public/test/ppapi_test_utils.h"
 #include "content/public/test/test_renderer_host.h"
 #include "extensions/common/constants.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "net/base/net_errors.h"
 #include "ppapi/shared_impl/test_utils.h"
 #include "rlz/buildflags/buildflags.h"
 #include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/network_service_test.mojom.h"
+#include "services/network/public/mojom/udp_socket.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 #if defined(OS_MACOSX)
@@ -381,6 +388,228 @@
 TEST_PPAPI_NACL(UDPSocketPrivate_Broadcast)
 TEST_PPAPI_NACL(UDPSocketPrivate_SetSocketFeatureErrors)
 
+namespace {
+
+// UDPSocket subclass that wraps a real network::mojom::UDPSocket, and can
+// simulate certain failures. Owns itself, and destroys itself when one of
+// its Mojo pipes is closed.
+class WrappedUDPSocket : public network::mojom::UDPSocket {
+ public:
+  // Type of failure to simulate. "DropPipe" failures correspond to dropping a
+  // Mojo pipe (Which typically happens if the network service crashes, or the
+  // parent NetworkContext is torn down). "Error" failures correspond to
+  // returning net::ERR_FAILED.
+  enum class FailureType {
+    kBindError,
+    kBindDropPipe,
+    kBroadcastError,
+    kBroadcastDropPipe,
+    kSendToDropPipe,
+    kSendToError,
+    kDropReceiverPipeOnConstruction,
+    kDropReceiverPipeOnReceiveMore,
+    kReadError,
+  };
+  WrappedUDPSocket(FailureType failure_type,
+                   network::mojom::NetworkContext* network_context,
+                   network::mojom::UDPSocketRequest socket_request,
+                   network::mojom::UDPSocketReceiverPtr socket_receiver)
+      : failure_type_(failure_type), binding_(this, std::move(socket_request)) {
+    if (failure_type == FailureType::kDropReceiverPipeOnConstruction)
+      socket_receiver.reset();
+    socket_receiver_ = std::move(socket_receiver);
+    network_context->CreateUDPSocket(mojo::MakeRequest(&wrapped_socket_),
+                                     nullptr);
+    binding_.set_connection_error_handler(
+        base::BindOnce(&WrappedUDPSocket::Close, base::Unretained(this)));
+    wrapped_socket_.set_connection_error_handler(
+        base::BindOnce(&WrappedUDPSocket::Close, base::Unretained(this)));
+  }
+
+  // network::mojom::UDPSocket implementation.
+  void Connect(const net::IPEndPoint& remote_addr,
+               network::mojom::UDPSocketOptionsPtr options,
+               ConnectCallback callback) override {
+    NOTREACHED();
+  }
+  void Bind(const net::IPEndPoint& local_addr,
+            network::mojom::UDPSocketOptionsPtr options,
+            BindCallback callback) override {
+    if (failure_type_ == FailureType::kBindError) {
+      std::move(callback).Run(net::ERR_FAILED, base::nullopt);
+      return;
+    }
+    if (failure_type_ == FailureType::kBindDropPipe) {
+      Close();
+      return;
+    }
+    wrapped_socket_->Bind(local_addr, std::move(options), std::move(callback));
+  }
+  void SetBroadcast(bool broadcast, SetBroadcastCallback callback) override {
+    if (failure_type_ == FailureType::kBroadcastError) {
+      std::move(callback).Run(net::ERR_FAILED);
+      return;
+    }
+    if (failure_type_ == FailureType::kBroadcastDropPipe) {
+      Close();
+      return;
+    }
+    wrapped_socket_->SetBroadcast(broadcast, std::move(callback));
+  }
+  void SetSendBufferSize(int32_t send_buffer_size,
+                         SetSendBufferSizeCallback callback) override {
+    wrapped_socket_->SetSendBufferSize(send_buffer_size, std::move(callback));
+  }
+  void SetReceiveBufferSize(int32_t receive_buffer_size,
+                            SetReceiveBufferSizeCallback callback) override {
+    wrapped_socket_->SetReceiveBufferSize(receive_buffer_size,
+                                          std::move(callback));
+  }
+  void JoinGroup(const net::IPAddress& group_address,
+                 JoinGroupCallback callback) override {
+    wrapped_socket_->JoinGroup(group_address, std::move(callback));
+  }
+  void LeaveGroup(const net::IPAddress& group_address,
+                  LeaveGroupCallback callback) override {
+    wrapped_socket_->LeaveGroup(group_address, std::move(callback));
+  }
+  void ReceiveMore(uint32_t num_additional_datagrams) override {
+    if (failure_type_ == FailureType::kDropReceiverPipeOnReceiveMore) {
+      socket_receiver_.reset();
+      return;
+    }
+    if (failure_type_ == FailureType::kReadError) {
+      for (uint32_t i = 0; i < num_additional_datagrams; ++i) {
+        socket_receiver_->OnReceived(net::ERR_FAILED, base::nullopt,
+                                     base::nullopt);
+      }
+      return;
+    }
+    // None of the tests using this fixture expect to read anything
+    // successfully, so just ignore this call if it isn't supposed to result in
+    // an error of some sort.
+  }
+  void ReceiveMoreWithBufferSize(uint32_t num_additional_datagrams,
+                                 uint32_t buffer_size) override {
+    NOTREACHED();
+  }
+  void SendTo(const net::IPEndPoint& dest_addr,
+              base::span<const uint8_t> data,
+              const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+              SendToCallback callback) override {
+    if (failure_type_ == FailureType::kSendToError) {
+      std::move(callback).Run(net::ERR_FAILED);
+      return;
+    }
+    if (failure_type_ == FailureType::kSendToDropPipe) {
+      Close();
+      return;
+    }
+    wrapped_socket_->SendTo(dest_addr, data, traffic_annotation,
+                            std::move(callback));
+  }
+  void Send(base::span<const uint8_t> data,
+            const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
+            SendCallback callback) override {
+    NOTREACHED();
+  }
+  void Close() override {
+    // Deleting |this| before closing the bindings can cause Mojo to DCHECK if
+    // there's a pending callback.
+    binding_.Close();
+    socket_receiver_.reset();
+    delete this;
+  }
+
+ private:
+  const FailureType failure_type_;
+  mojo::Binding<network::mojom::UDPSocket> binding_;
+  network::mojom::UDPSocketPtr wrapped_socket_;
+
+  // Only populated on certain read FailureTypes.
+  network::mojom::UDPSocketReceiverPtr socket_receiver_;
+
+  DISALLOW_COPY_AND_ASSIGN(WrappedUDPSocket);
+};
+
+void TestCreateUDPSocketCallback(
+    WrappedUDPSocket::FailureType failure_type,
+    network::mojom::NetworkContext* network_context,
+    network::mojom::UDPSocketRequest socket_request,
+    network::mojom::UDPSocketReceiverPtr socket_receiver) {
+  // This will delete itself when one of its Mojo pipes is closed.
+  new WrappedUDPSocket(failure_type, network_context, std::move(socket_request),
+                       std::move(socket_receiver));
+}
+
+#define RUN_UDP_FAILURE_TEST(test_name, failure_type)                    \
+  do {                                                                   \
+    auto callback =                                                      \
+        base::BindRepeating(&TestCreateUDPSocketCallback, failure_type); \
+    ppapi::SetPepperUDPSocketCallackForTesting(&callback);               \
+    RunTestViaHTTP(LIST_TEST(test_name));                                \
+    ppapi::SetPepperUDPSocketCallackForTesting(nullptr);                 \
+  } while (false)
+
+}  // namespace
+
+// Macro for tests that use |WrappedUDPSocket| to simulate errors. |test_name|
+// and |_test| are separate values because there are often multiple ways to get
+// the same error pattern (Dropped mojo pipe and failed call, generally).
+#define UDPSOCKET_FAILURE_TEST(test_name, _test, failure_type)               \
+  IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, test_name) {                 \
+    RUN_UDP_FAILURE_TEST(_test, failure_type);                               \
+  }                                                                          \
+  IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, MAYBE_PPAPI_NACL(test_name)) { \
+    RUN_UDP_FAILURE_TEST(_test, failure_type);                               \
+  }                                                                          \
+  IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(test_name)) {       \
+    RUN_UDP_FAILURE_TEST(_test, failure_type);                               \
+  }                                                                          \
+  IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClTest, MAYBE_PPAPI_PNACL(test_name)) { \
+    RUN_UDP_FAILURE_TEST(_test, failure_type);                               \
+  }                                                                          \
+  IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClNonSfiTest,                           \
+                         MAYBE_PNACL_NONSFI(test_name)) {                    \
+    RUN_UDP_FAILURE_TEST(_test, failure_type);                               \
+  }
+
+UDPSOCKET_FAILURE_TEST(UDPSocket_BindError,
+                       UDPSocket_BindFails,
+                       WrappedUDPSocket::FailureType::kBindError);
+UDPSOCKET_FAILURE_TEST(UDPSocket_BindDropPipe,
+                       UDPSocket_BindFails,
+                       WrappedUDPSocket::FailureType::kBindDropPipe);
+UDPSOCKET_FAILURE_TEST(UDPSocket_BroadcastBeforeBindError,
+                       UDPSocket_BroadcastBeforeBindFails,
+                       WrappedUDPSocket::FailureType::kBroadcastError);
+UDPSOCKET_FAILURE_TEST(UDPSocket_BroadcastBeforeBindDropPipe,
+                       UDPSocket_BroadcastBeforeBindFails,
+                       WrappedUDPSocket::FailureType::kBroadcastDropPipe);
+UDPSOCKET_FAILURE_TEST(UDPSocket_BroadcastAfterBindError,
+                       UDPSocket_BroadcastAfterBindFails,
+                       WrappedUDPSocket::FailureType::kBroadcastError);
+UDPSOCKET_FAILURE_TEST(UDPSocket_BroadcastAfterBindDropPipe,
+                       UDPSocket_BroadcastAfterBindFails,
+                       WrappedUDPSocket::FailureType::kBroadcastDropPipe);
+UDPSOCKET_FAILURE_TEST(UDPSocket_SendToBeforeDropPipeFails,
+                       UDPSocket_SendToFails,
+                       WrappedUDPSocket::FailureType::kSendToDropPipe);
+UDPSOCKET_FAILURE_TEST(UDPSocket_DropPipeAfterBindSendToFails,
+                       UDPSocket_SendToFails,
+                       WrappedUDPSocket::FailureType::kSendToError);
+UDPSOCKET_FAILURE_TEST(UDPSocket_ReadError,
+                       UDPSocket_ReadFails,
+                       WrappedUDPSocket::FailureType::kReadError);
+UDPSOCKET_FAILURE_TEST(
+    UDPSocket_DropReceiverPipeOnConstruction,
+    UDPSocket_ReadFails,
+    WrappedUDPSocket::FailureType::kDropReceiverPipeOnConstruction);
+UDPSOCKET_FAILURE_TEST(
+    UDPSocket_DropReceiverPipeOnReceiveMore,
+    UDPSocket_ReadFails,
+    WrappedUDPSocket::FailureType::kDropReceiverPipeOnReceiveMore);
+
 // Disallowed socket tests.
 TEST_PPAPI_NACL_DISALLOWED_SOCKETS(HostResolverPrivateDisallowed)
 TEST_PPAPI_NACL_DISALLOWED_SOCKETS(TCPServerSocketPrivateDisallowed)
diff --git a/chromecast/common/extensions_api/BUILD.gn b/chromecast/common/extensions_api/BUILD.gn
index 8863c28..927efac 100644
--- a/chromecast/common/extensions_api/BUILD.gn
+++ b/chromecast/common/extensions_api/BUILD.gn
@@ -31,12 +31,9 @@
 
 extensions_api_uncompiled_sources = [ "extension.json" ]
 
-json_schema_api("generated_api_strings_bundle") {
-  sources = schema_sources
-  uncompiled_sources = extensions_api_uncompiled_sources
-  bundle = true
+generated_json_strings("generated_api_json_strings") {
+  sources = schema_sources + extensions_api_uncompiled_sources + [ "tts.json" ]
   bundle_name = "Cast"
-  uncompiled_bundle_schema_sources = [ "tts.json" ]
   schema_include_rules = extensions_api_schema_include_rules
 
   deps = [
@@ -45,10 +42,9 @@
   visibility = [ ":api" ]
 }
 
-json_schema_api("generated_api_registration_bundle") {
+function_registration("generated_api_registration") {
   sources = schema_sources
   impl_dir = "//chromecast/browser/extensions/api"
-  bundle_registration = true
   bundle_name = "Cast"
 
   deps = [
@@ -96,8 +92,8 @@
 
 group("api") {
   public_deps = [
-    ":generated_api_registration_bundle",
-    ":generated_api_strings_bundle",
+    ":generated_api_json_strings",
+    ":generated_api_registration",
     ":generated_api_types",
   ]
 }
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index d55998a5..a91692b 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -224,6 +224,8 @@
     "dbus/fake_media_analytics_client.h",
     "dbus/fake_modem_messaging_client.cc",
     "dbus/fake_modem_messaging_client.h",
+    "dbus/fake_oobe_configuration_client.cc",
+    "dbus/fake_oobe_configuration_client.h",
     "dbus/fake_permission_broker_client.cc",
     "dbus/fake_permission_broker_client.h",
     "dbus/fake_power_manager_client.cc",
@@ -268,6 +270,8 @@
     "dbus/media_analytics_client.h",
     "dbus/modem_messaging_client.cc",
     "dbus/modem_messaging_client.h",
+    "dbus/oobe_configuration_client.cc",
+    "dbus/oobe_configuration_client.h",
     "dbus/permission_broker_client.cc",
     "dbus/permission_broker_client.h",
     "dbus/pipe_reader.cc",
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 2b06cbc4..5131ab9 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -518,9 +518,6 @@
 // Indicates that if we should start bootstrapping Master OOBE.
 const char kOobeBootstrappingMaster[] = "oobe-bootstrapping-master";
 
-// Path to a OOBE configuration JSON file.
-const char kOobeConfiguration[] = "oobe-configuration-file";
-
 // Forces OOBE/login to force show a comma-separated list of screens from
 // chromeos::kScreenNames in oobe_screen.cc. Supported screens are:
 //   user-image
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 3dffb29..fe6df65 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -145,7 +145,6 @@
 CHROMEOS_EXPORT extern const char kNeedArcMigrationPolicyCheck[];
 CHROMEOS_EXPORT extern const char kNoteTakingAppIds[];
 CHROMEOS_EXPORT extern const char kOobeBootstrappingMaster[];
-CHROMEOS_EXPORT extern const char kOobeConfiguration[];
 CHROMEOS_EXPORT extern const char kOobeForceShowScreen[];
 CHROMEOS_EXPORT extern const char kOobeGuestSession[];
 CHROMEOS_EXPORT extern const char kOobeSkipPostLogin[];
diff --git a/chromeos/dbus/dbus_clients_browser.cc b/chromeos/dbus/dbus_clients_browser.cc
index 23b29a53a..4c055b2 100644
--- a/chromeos/dbus/dbus_clients_browser.cc
+++ b/chromeos/dbus/dbus_clients_browser.cc
@@ -30,12 +30,14 @@
 #include "chromeos/dbus/fake_image_loader_client.h"
 #include "chromeos/dbus/fake_lorgnette_manager_client.h"
 #include "chromeos/dbus/fake_media_analytics_client.h"
+#include "chromeos/dbus/fake_oobe_configuration_client.h"
 #include "chromeos/dbus/fake_smb_provider_client.h"
 #include "chromeos/dbus/fake_virtual_file_provider_client.h"
 #include "chromeos/dbus/image_burner_client.h"
 #include "chromeos/dbus/image_loader_client.h"
 #include "chromeos/dbus/lorgnette_manager_client.h"
 #include "chromeos/dbus/media_analytics_client.h"
+#include "chromeos/dbus/oobe_configuration_client.h"
 #include "chromeos/dbus/smb_provider_client.h"
 #include "chromeos/dbus/virtual_file_provider_client.h"
 
@@ -114,6 +116,11 @@
     media_analytics_client_.reset(new FakeMediaAnalyticsClient);
 
   if (use_real_clients)
+    oobe_configuration_client_ = OobeConfigurationClient::Create();
+  else
+    oobe_configuration_client_.reset(new FakeOobeConfigurationClient);
+
+  if (use_real_clients)
     smb_provider_client_.reset(SmbProviderClient::Create());
   else
     smb_provider_client_ = std::make_unique<FakeSmbProviderClient>();
@@ -143,6 +150,7 @@
   image_loader_client_->Init(system_bus);
   lorgnette_manager_client_->Init(system_bus);
   media_analytics_client_->Init(system_bus);
+  oobe_configuration_client_->Init(system_bus);
   smb_provider_client_->Init(system_bus);
   virtual_file_provider_client_->Init(system_bus);
 }
diff --git a/chromeos/dbus/dbus_clients_browser.h b/chromeos/dbus/dbus_clients_browser.h
index 8593da79..ce060241 100644
--- a/chromeos/dbus/dbus_clients_browser.h
+++ b/chromeos/dbus/dbus_clients_browser.h
@@ -30,6 +30,7 @@
 class ImageLoaderClient;
 class LorgnetteManagerClient;
 class MediaAnalyticsClient;
+class OobeConfigurationClient;
 class SmbProviderClient;
 class VirtualFileProviderClient;
 
@@ -62,6 +63,7 @@
   std::unique_ptr<ImageLoaderClient> image_loader_client_;
   std::unique_ptr<LorgnetteManagerClient> lorgnette_manager_client_;
   std::unique_ptr<MediaAnalyticsClient> media_analytics_client_;
+  std::unique_ptr<OobeConfigurationClient> oobe_configuration_client_;
   std::unique_ptr<SmbProviderClient> smb_provider_client_;
   std::unique_ptr<VirtualFileProviderClient> virtual_file_provider_client_;
 
diff --git a/chromeos/dbus/dbus_switches.cc b/chromeos/dbus/dbus_switches.cc
index a6fdc3f..404b6225 100644
--- a/chromeos/dbus/dbus_switches.cc
+++ b/chromeos/dbus/dbus_switches.cc
@@ -14,6 +14,9 @@
 // Forces the stub implementation of D-Bus clients.
 const char kDbusStub[] = "dbus-stub";
 
+// Path to a OOBE configuration JSON file (used by FakeOobeConfigurationClient).
+const char kFakeOobeConfiguration[] = "fake-oobe-configuration-file";
+
 // Overrides Shill stub behavior. By default, ethernet, wifi and vpn are
 // enabled, and transitions occur instantaneously. Multiple options can be
 // comma separated (no spaces). Note: all options are in the format 'foo=x'.
diff --git a/chromeos/dbus/dbus_switches.h b/chromeos/dbus/dbus_switches.h
index 8da95077..bfb08d9 100644
--- a/chromeos/dbus/dbus_switches.h
+++ b/chromeos/dbus/dbus_switches.h
@@ -12,6 +12,7 @@
 
 CHROMEOS_EXPORT extern const char kAttestationServer[];
 CHROMEOS_EXPORT extern const char kDbusStub[];
+CHROMEOS_EXPORT extern const char kFakeOobeConfiguration[];
 CHROMEOS_EXPORT extern const char kShillStub[];
 CHROMEOS_EXPORT extern const char kSmsTestMessages[];
 CHROMEOS_EXPORT extern const char kSystemDevMode[];
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index 50811374..d3b34b8 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -242,6 +242,10 @@
   return clients_common_->modem_messaging_client_.get();
 }
 
+OobeConfigurationClient* DBusThreadManager::GetOobeConfigurationClient() {
+  return clients_browser_->oobe_configuration_client_.get();
+}
+
 PermissionBrokerClient* DBusThreadManager::GetPermissionBrokerClient() {
   return clients_common_->permission_broker_client_.get();
 }
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index 643d7304..6a036452 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -49,6 +49,7 @@
 class MachineLearningClient;
 class MediaAnalyticsClient;
 class ModemMessagingClient;
+class OobeConfigurationClient;
 class PermissionBrokerClient;
 class PowerManagerClient;
 class SessionManagerClient;
@@ -154,6 +155,7 @@
   MachineLearningClient* GetMachineLearningClient();
   MediaAnalyticsClient* GetMediaAnalyticsClient();
   ModemMessagingClient* GetModemMessagingClient();
+  OobeConfigurationClient* GetOobeConfigurationClient();
   PermissionBrokerClient* GetPermissionBrokerClient();
   PowerManagerClient* GetPowerManagerClient();
   SessionManagerClient* GetSessionManagerClient();
diff --git a/chromeos/dbus/fake_easy_unlock_client.h b/chromeos/dbus/fake_easy_unlock_client.h
index 8b9ad6e..c790267 100644
--- a/chromeos/dbus/fake_easy_unlock_client.h
+++ b/chromeos/dbus/fake_easy_unlock_client.h
@@ -12,7 +12,7 @@
 
 namespace chromeos {
 
-// A fake implemetation of EasyUnlockClient.
+// A fake implementation of EasyUnlockClient.
 class CHROMEOS_EXPORT FakeEasyUnlockClient : public EasyUnlockClient {
  public:
   // Tests if the provided keys belong to the same (fake) EC P256 key pair
diff --git a/chromeos/dbus/fake_oobe_configuration_client.cc b/chromeos/dbus/fake_oobe_configuration_client.cc
new file mode 100644
index 0000000..b44dc4e
--- /dev/null
+++ b/chromeos/dbus/fake_oobe_configuration_client.cc
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/dbus/fake_oobe_configuration_client.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/task/post_task.h"
+#include "chromeos/dbus/dbus_switches.h"
+
+namespace {
+
+std::string LoadConfigurationFile(base::FilePath path) {
+  std::string configuration_data;
+  if (!base::ReadFileToString(path, &configuration_data)) {
+    DLOG(WARNING) << "Can't read OOBE Configuration";
+    return std::string();
+  }
+  return configuration_data;
+}
+
+void OnConfigurationLoaded(
+    chromeos::OobeConfigurationClient::ConfigurationCallback callback,
+    const std::string& configuration) {
+  std::move(callback).Run(!configuration.empty(), configuration);
+}
+
+}  // namespace
+
+namespace chromeos {
+
+FakeOobeConfigurationClient::FakeOobeConfigurationClient() = default;
+
+FakeOobeConfigurationClient::~FakeOobeConfigurationClient() = default;
+
+void FakeOobeConfigurationClient::Init(dbus::Bus* bus) {}
+
+void FakeOobeConfigurationClient::CheckForOobeConfiguration(
+    ConfigurationCallback callback) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kFakeOobeConfiguration)) {
+    std::move(callback).Run(false, std::string());
+    return;
+  }
+
+  const base::FilePath path =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+          chromeos::switches::kFakeOobeConfiguration);
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
+      base::BindOnce(&LoadConfigurationFile, path),
+      base::BindOnce(&OnConfigurationLoaded, std::move(callback)));
+}
+
+}  // namespace chromeos
diff --git a/chromeos/dbus/fake_oobe_configuration_client.h b/chromeos/dbus/fake_oobe_configuration_client.h
new file mode 100644
index 0000000..7b0752f
--- /dev/null
+++ b/chromeos/dbus/fake_oobe_configuration_client.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_DBUS_FAKE_OOBE_CONFIGURATION_CLIENT_H_
+#define CHROMEOS_DBUS_FAKE_OOBE_CONFIGURATION_CLIENT_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chromeos/dbus/oobe_configuration_client.h"
+
+namespace chromeos {
+
+// A fake implementation of OobeConfigurationClient, provides configuration
+// specified via command-line flag.
+class CHROMEOS_EXPORT FakeOobeConfigurationClient
+    : public OobeConfigurationClient {
+ public:
+  FakeOobeConfigurationClient();
+  ~FakeOobeConfigurationClient() override;
+
+  void Init(dbus::Bus* bus) override;
+
+  // EasyUnlockClient overrides
+  void CheckForOobeConfiguration(ConfigurationCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeOobeConfigurationClient);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_FAKE_OOBE_CONFIGURATION_CLIENT_H_
diff --git a/chromeos/dbus/oobe_configuration_client.cc b/chromeos/dbus/oobe_configuration_client.cc
new file mode 100644
index 0000000..3590c2b
--- /dev/null
+++ b/chromeos/dbus/oobe_configuration_client.cc
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/dbus/oobe_configuration_client.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+// The OobeConfigurationClient used in production.
+
+class OobeConfigurationClientImpl : public OobeConfigurationClient {
+ public:
+  OobeConfigurationClientImpl() : weak_ptr_factory_(this) {}
+
+  ~OobeConfigurationClientImpl() override = default;
+
+  // OobeConfigurationClient override:
+  void CheckForOobeConfiguration(ConfigurationCallback callback) override {
+    // TODO (antrim): do a method call once https://crbug.com/869209 is fixed.
+    OnData(std::move(callback), nullptr);
+  }
+
+ protected:
+  void Init(dbus::Bus* bus) override {
+    // TODO (antrim): get proxy object once https://crbug.com/869209 is fixed.
+  }
+
+ private:
+  void OnData(ConfigurationCallback callback, dbus::Response* response) {
+    if (!response) {
+      std::move(callback).Run(false, std::string());
+      return;
+    }
+
+    // TODO (antrim): read response once https://crbug.com/869209 is fixed.
+    NOTREACHED();
+  }
+
+  // Note: This should remain the last member so it'll be destroyed and
+  // invalidate its weak pointers before any other members are destroyed.
+  base::WeakPtrFactory<OobeConfigurationClientImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OobeConfigurationClientImpl);
+};
+
+// static
+std::unique_ptr<OobeConfigurationClient> OobeConfigurationClient::Create() {
+  return std::make_unique<OobeConfigurationClientImpl>();
+}
+
+}  // namespace chromeos
diff --git a/chromeos/dbus/oobe_configuration_client.h b/chromeos/dbus/oobe_configuration_client.h
new file mode 100644
index 0000000..fa027a0
--- /dev/null
+++ b/chromeos/dbus/oobe_configuration_client.h
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_DBUS_OOBE_CONFIGURATION_CLIENT_H_
+#define CHROMEOS_DBUS_OOBE_CONFIGURATION_CLIENT_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
+
+namespace chromeos {
+
+// Client for calling OobeConfiguration dbus service. The service provides
+// verified OOBE configuration, that allows to automate out-of-box experience.
+// This configuration comes either from the state before power wash, or from
+// USB stick during USB-based enrollment flow.
+
+class CHROMEOS_EXPORT OobeConfigurationClient : public DBusClient {
+ public:
+  using ConfigurationCallback =
+      base::OnceCallback<void(bool has_configuration,
+                              const std::string& configuration)>;
+
+  ~OobeConfigurationClient() override = default;
+
+  // Factory function.
+  static std::unique_ptr<OobeConfigurationClient> Create();
+
+  // Checks if valid OOBE configuration exists.
+  virtual void CheckForOobeConfiguration(ConfigurationCallback callback) = 0;
+
+ protected:
+  // Create() should be used instead.
+  OobeConfigurationClient() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OobeConfigurationClient);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_OOBE_CONFIGURATION_CLIENT_H_
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index f2970e1..b88c048 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -731,6 +731,10 @@
             mojom::AssistantInteractionResolution::kError);
       });
       break;
+    // The device was not elected to produce a response.
+    case Resolution::DEVICE_NOT_SELECTED:
+      // TODO(b/112952143): handle this case appropriately.
+      break;
   }
 }
 
diff --git a/components/arc/common/BUILD.gn b/components/arc/common/BUILD.gn
index c93e2bb..4ac40443 100644
--- a/components/arc/common/BUILD.gn
+++ b/components/arc/common/BUILD.gn
@@ -29,6 +29,7 @@
       "file_system.mojom",
       "ime.mojom",
       "input_method_manager.mojom",
+      "intent_common.mojom",
       "intent_helper.mojom",
       "kiosk.mojom",
       "lock_screen.mojom",
@@ -64,6 +65,7 @@
       "//device/usb/public/mojom",
       "//mojo/public/mojom/base",
       "//ui/gfx/geometry/mojo",
+      "//url/mojom:url_mojom_gurl",
     ]
   }
 
diff --git a/components/arc/common/file_system.mojom b/components/arc/common/file_system.mojom
index 246bd13f..870f306 100644
--- a/components/arc/common/file_system.mojom
+++ b/components/arc/common/file_system.mojom
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 8
+// Next MinVersion: 9
 
 module arc.mojom;
 
+import "components/arc/common/intent_common.mojom";
+import "url/mojom/url.mojom";
+
 // Represents a document in Android DocumentsProvider.
 // See Android docs of DocumentsContract.Document for details.
 struct Document {
@@ -49,6 +52,23 @@
   DELETED = 1,
 };
 
+// Content URL associated with its MIME type.
+struct ContentUrlWithMimeType {
+  url.mojom.Url content_url;
+  string mime_type;
+};
+
+// Request for opening URLs by sending an intent to the specified activity.
+struct OpenUrlsRequest {
+  // Action type of the intent.
+  ActionType action_type;
+
+  // Target activity for the intent.
+  ActivityName activity_name;
+
+  // One or more URLs to open with the intent.
+  array<ContentUrlWithMimeType> urls;
+};
 
 // Next method ID: 5
 interface FileSystemHost {
@@ -73,7 +93,7 @@
   [MinVersion=6] OpenFileToRead@4(string url) => (handle? fd);
 };
 
-// Next method ID: 11
+// Next method ID: 12
 interface FileSystemInstance {
   // Notes about Android Documents Provider:
   //
@@ -163,4 +183,10 @@
   // When the specified file does not exist, the corresponding entry in
   // MediaProvider is removed.
   RequestMediaScan@0(array<string> paths);
+
+  // Opens URLs by sending an intent to the specified activity.
+  // Since this grants read/write URL permissions to the activity, callers must
+  // ensure that the user is correctly aware of the URLs and the activity they
+  // are passing.
+  [MinVersion=8] OpenUrlsWithPermission@11(OpenUrlsRequest request) => ();
 };
diff --git a/components/arc/common/intent_common.mojom b/components/arc/common/intent_common.mojom
new file mode 100644
index 0000000..d18b11e
--- /dev/null
+++ b/components/arc/common/intent_common.mojom
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module arc.mojom;
+
+// Describes the type of action to invoke.
+[Extensible]
+enum ActionType {
+  VIEW,           // Can handle only one URL.
+  SEND,           // Can handle only one URL.
+  SEND_MULTIPLE,  // Can handle multiple URLs.
+};
+
+// Describes an activity.
+struct ActivityName {
+  string package_name;
+  string? activity_name;  // may be null to indicate any activity within package
+};
diff --git a/components/arc/common/intent_helper.mojom b/components/arc/common/intent_helper.mojom
index 5547d80..ce61d6a 100644
--- a/components/arc/common/intent_helper.mojom
+++ b/components/arc/common/intent_helper.mojom
@@ -6,15 +6,9 @@
 
 module arc.mojom;
 
+import "components/arc/common/intent_common.mojom";
 import "components/arc/common/scale_factor.mojom";
 
-// Describes the type of action to invoke.
-enum ActionType {
-  VIEW,
-  SEND,
-  SEND_MULTIPLE,
-};
-
 [Extensible]
 enum PatternType {
   PATTERN_LITERAL,
@@ -69,12 +63,6 @@
   [MinVersion=14] string? fallback_url;
 };
 
-// Describes an activity.
-struct ActivityName {
-  string package_name;
-  string? activity_name;  // may be null to indicate any activity within package
-};
-
 // Describes an icon for the activity.
 struct ActivityIcon {
   ActivityName activity;
@@ -198,10 +186,10 @@
   // most suitable activity for the URL within the package will be started.
   [MinVersion=2] HandleUrl@2(string url, string package_name);
 
-  // Handles the list of URLs by sending a specified intent to the handler.
-  [MinVersion=5] HandleUrlList@7(array<UrlWithMimeType> urls,
-                                 ActivityName activity,
-                                 ActionType action_type);
+  // DEPRECATED. Use FileSystemInstance.OpenUrlsWithPermission() instead.
+  [MinVersion=5] HandleUrlListDeprecated@7(array<UrlWithMimeType> urls,
+                                           ActivityName activity,
+                                           ActionType action_type);
 
   // DEPRECATED: Please use Init@13 instead.
   InitDeprecated@0(IntentHelperHost host_ptr);
diff --git a/components/arc/test/fake_file_system_instance.cc b/components/arc/test/fake_file_system_instance.cc
index dfcff8d4..8d28cb4e 100644
--- a/components/arc/test/fake_file_system_instance.cc
+++ b/components/arc/test/fake_file_system_instance.cc
@@ -307,4 +307,10 @@
   // Do nothing and pretend we scaned them.
 }
 
+void FakeFileSystemInstance::OpenUrlsWithPermission(
+    mojom::OpenUrlsRequestPtr request,
+    OpenUrlsWithPermissionCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
 }  // namespace arc
diff --git a/components/arc/test/fake_file_system_instance.h b/components/arc/test/fake_file_system_instance.h
index aa77d31b..2c222e2 100644
--- a/components/arc/test/fake_file_system_instance.h
+++ b/components/arc/test/fake_file_system_instance.h
@@ -159,6 +159,8 @@
   void RemoveWatcher(int64_t watcher_id,
                      RemoveWatcherCallback callback) override;
   void RequestMediaScan(const std::vector<std::string>& paths) override;
+  void OpenUrlsWithPermission(mojom::OpenUrlsRequestPtr request,
+                              OpenUrlsWithPermissionCallback callback) override;
 
  private:
   // A pair of an authority and a document ID which identifies the location
diff --git a/components/arc/test/fake_intent_helper_instance.cc b/components/arc/test/fake_intent_helper_instance.cc
index 8779fbe..c45e6f3 100644
--- a/components/arc/test/fake_intent_helper_instance.cc
+++ b/components/arc/test/fake_intent_helper_instance.cc
@@ -67,7 +67,7 @@
 void FakeIntentHelperInstance::HandleUrl(const std::string& url,
                                          const std::string& package_name) {}
 
-void FakeIntentHelperInstance::HandleUrlList(
+void FakeIntentHelperInstance::HandleUrlListDeprecated(
     std::vector<mojom::UrlWithMimeTypePtr> urls,
     mojom::ActivityNamePtr activity,
     mojom::ActionType action) {}
diff --git a/components/arc/test/fake_intent_helper_instance.h b/components/arc/test/fake_intent_helper_instance.h
index 9d73d9ad..67872ca1 100644
--- a/components/arc/test/fake_intent_helper_instance.h
+++ b/components/arc/test/fake_intent_helper_instance.h
@@ -78,9 +78,9 @@
   void HandleUrl(const std::string& url,
                  const std::string& package_name) override;
 
-  void HandleUrlList(std::vector<mojom::UrlWithMimeTypePtr> urls,
-                     mojom::ActivityNamePtr activity,
-                     mojom::ActionType action) override;
+  void HandleUrlListDeprecated(std::vector<mojom::UrlWithMimeTypePtr> urls,
+                               mojom::ActivityNamePtr activity,
+                               mojom::ActionType action) override;
 
   void InitDeprecated(mojom::IntentHelperHostPtr host_ptr) override;
 
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index e1175f5..73d0a48 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -122,6 +122,8 @@
  protected:
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  // Issue an UnmaskCard request. This requires an OAuth token before starting
+  // the request.
   void StartUnmasking() {
     if (!identity_test_env_.identity_manager()->HasPrimaryAccount())
       identity_test_env_.MakePrimaryAccountAvailable("example@gmail.com");
@@ -136,6 +138,7 @@
                                        weak_ptr_factory_.GetWeakPtr()));
   }
 
+  // Issue a GetUploadDetails request.
   void StartGettingUploadDetails() {
     if (!identity_test_env_.identity_manager()->HasPrimaryAccount())
       identity_test_env_.MakePrimaryAccountAvailable("example@gmail.com");
@@ -149,6 +152,8 @@
         /*billable_service_number=*/12345);
   }
 
+  // Issue an UploadCard request. This requires an OAuth token before starting
+  // the request.
   void StartUploading(bool include_cvc) {
     if (!identity_test_env_.identity_manager()->HasPrimaryAccount())
       identity_test_env_.MakePrimaryAccountAvailable("example@gmail.com");
@@ -173,6 +178,8 @@
 
   net::HttpRequestHeaders* GetRequestHeaders() { return &intercepted_headers_; }
 
+  // Issues access token in response to any access token request. This will
+  // start the Payments Request which requires the authentication.
   void IssueOAuthToken() {
     identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
         "totally_real_token",
@@ -508,7 +515,9 @@
 
 TEST_F(PaymentsClientTest, UploadDoesNotIncludeCvcInRequestIfNotProvided) {
   StartUploading(/*include_cvc=*/false);
+  IssueOAuthToken();
 
+  EXPECT_TRUE(!GetUploadData().empty());
   // Verify that the encrypted_cvc and s7e_13_cvc parameters were not included
   // in the request.
   EXPECT_TRUE(GetUploadData().find("encrypted_cvc") == std::string::npos);
diff --git a/components/autofill/core/browser/suggestion.cc b/components/autofill/core/browser/suggestion.cc
index 8d6959e1..5f36fe5 100644
--- a/components/autofill/core/browser/suggestion.cc
+++ b/components/autofill/core/browser/suggestion.cc
@@ -9,10 +9,7 @@
 namespace autofill {
 
 Suggestion::Suggestion()
-    : frontend_id(0),
-      match(PREFIX_MATCH),
-      is_value_bold(false) {
-}
+    : frontend_id(0), match(PREFIX_MATCH), is_value_secondary(false) {}
 
 Suggestion::Suggestion(const Suggestion& other)
     : backend_id(other.backend_id),
@@ -22,14 +19,13 @@
       custom_icon(other.custom_icon),
       icon(other.icon),
       match(other.match),
-      is_value_bold(other.is_value_bold) {}
+      is_value_secondary(other.is_value_secondary) {}
 
 Suggestion::Suggestion(const base::string16& v)
     : frontend_id(0),
       value(v),
       match(PREFIX_MATCH),
-      is_value_bold(false) {
-}
+      is_value_secondary(false) {}
 
 Suggestion::Suggestion(const std::string& v,
                        const std::string& l,
@@ -40,8 +36,7 @@
       label(base::UTF8ToUTF16(l)),
       icon(base::UTF8ToUTF16(i)),
       match(PREFIX_MATCH),
-      is_value_bold(false) {
-}
+      is_value_secondary(false) {}
 
 Suggestion::~Suggestion() = default;
 
diff --git a/components/autofill/core/browser/suggestion.h b/components/autofill/core/browser/suggestion.h
index 58dfbf9..7c65c47 100644
--- a/components/autofill/core/browser/suggestion.h
+++ b/components/autofill/core/browser/suggestion.h
@@ -51,7 +51,7 @@
   // If |custom_icon| is empty, the name of the fallback built-in icon.
   base::string16 icon;
   MatchMode match;
-  bool is_value_bold;  // true if |value| should be displayed in bold type face.
+  bool is_value_secondary;  // |value| should be displayed as secondary text.
 };
 
 }  // namespace autofill
diff --git a/components/autofill/ios/form_util/DEPS b/components/autofill/ios/form_util/DEPS
deleted file mode 100644
index 4c30da6..0000000
--- a/components/autofill/ios/form_util/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-specific_include_rules = {
-  # Unittests using form.js need page_script_util to inject the script.
-  "form_unittest\.mm": [
-    "+ios/web/web_state/js/page_script_util.h",
-  ],
-}
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
index 27731e1..38e9f60 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
@@ -10,17 +10,26 @@
 #import "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "ios/web/public/test/fakes/test_web_state_observer_util.h"
+#import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #include "testing/platform_test.h"
 
+class FormTestClient : public web::TestWebClient {
+ public:
+  NSString* GetDocumentStartScriptForAllFrames(
+      web::BrowserState* browser_state) const override {
+    return web::test::GetPageScript(@"form");
+  }
+};
+
 // Test fixture for autofill::FormActivityTabHelper class.
 class FormActivityTabHelperTest
     : public web::WebJsTest<web::WebTestWithWebState> {
  public:
   FormActivityTabHelperTest()
       : web::WebJsTest<web::WebTestWithWebState>(
-            @[ @"chrome_bundle_all_frames" ]) {}
+            std::make_unique<FormTestClient>()) {}
 
   void SetUp() override {
     web::WebJsTest<web::WebTestWithWebState>::SetUp();
diff --git a/components/autofill/ios/form_util/form_unittest.mm b/components/autofill/ios/form_util/form_unittest.mm
index 6ff183c..6f535c37 100644
--- a/components/autofill/ios/form_util/form_unittest.mm
+++ b/components/autofill/ios/form_util/form_unittest.mm
@@ -7,9 +7,9 @@
 #import "ios/web/public/browser_state.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
 #include "ios/web/public/test/fakes/test_web_state_observer.h"
+#import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
-#import "ios/web/web_state/js/page_script_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,7 +20,7 @@
  public:
   NSString* GetDocumentStartScriptForAllFrames(
       web::BrowserState* browser_state) const override {
-    return web::GetPageScript(@"form");
+    return web::test::GetPageScript(@"form");
   }
 };
 
diff --git a/components/omnibox/browser/search_suggestion_parser.h b/components/omnibox/browser/search_suggestion_parser.h
index d24e679..c14a38c 100644
--- a/components/omnibox/browser/search_suggestion_parser.h
+++ b/components/omnibox/browser/search_suggestion_parser.h
@@ -287,7 +287,10 @@
     // calls SortResults, so order always holds except possibly while parsing.
     SuggestResults suggest_results;
 
-    // Navigational suggestions sorted by relevance score.
+    // Navigational suggestions sorted by relevance score, descending. This
+    // order is normally provided by server and is guaranteed after search
+    // provider calls SortResults, so order always holds except possibly while
+    // parsing.
     NavigationResults navigation_results;
 
     // The server supplied verbatim relevance scores. Negative values
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index 29b674f..a245a08 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -50,8 +50,10 @@
 
 // Returns |username| unless it is empty. For an empty |username| returns a
 // localised string saying this username is empty. Use this for displaying the
-// usernames to the user.
-base::string16 ReplaceEmptyUsername(const base::string16& username) {
+// usernames to the user. |replaced| is set to true iff |username| is empty.
+base::string16 ReplaceEmptyUsername(const base::string16& username,
+                                    bool* replaced) {
+  *replaced = username.empty();
   if (username.empty())
     return l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_EMPTY_LOGIN);
   return username;
@@ -92,7 +94,10 @@
   base::string16 lower_contents = base::i18n::ToLower(field_contents);
   if (show_all || autofill::FieldIsSuggestionSubstringStartingOnTokenBoundary(
                       lower_suggestion, lower_contents, true)) {
-    autofill::Suggestion suggestion(ReplaceEmptyUsername(field_suggestion));
+    bool replaced_username;
+    autofill::Suggestion suggestion(
+        ReplaceEmptyUsername(field_suggestion, &replaced_username));
+    suggestion.is_value_secondary = replaced_username;
     suggestion.label =
         signon_realm.empty()
             ? base::string16(password_length, kPasswordReplacementChar)
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 866e532..b0651b4 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -163,8 +163,10 @@
 
   metrics_recorder_->RecordFormSignature(observed_form_signature_);
 
-  if (owned_form_fetcher_)
+  if (owned_form_fetcher_ &&
+      !observed_form_.is_gaia_with_skip_save_password_form) {
     owned_form_fetcher_->Fetch();
+  }
   form_fetcher_->AddConsumer(this);
 }
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 6272a8f0..702f00f 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -671,9 +671,6 @@
                        base::CompareCase::SENSITIVE))
       continue;
 
-    if (form.is_gaia_with_skip_save_password_form)
-      continue;
-
     bool old_manager_found = false;
     for (const auto& old_manager : pending_login_managers_) {
       if (old_manager->DoesManage(form, driver) !=
@@ -723,6 +720,9 @@
   // Find new forms.
   std::vector<const PasswordForm*> new_forms;
   for (const PasswordForm& form : forms) {
+    // TODO(https://crbug.com/831123): Implement inside NewPasswordFormManger
+    // not-filling Gaia forms that should be ignored instead of non-creating
+    // NewPasswordFormManger instance.
     if (form.is_gaia_with_skip_save_password_form)
       continue;
     auto form_it =
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index f18f193..8247dcd 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -856,6 +856,37 @@
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 }
 
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+TEST_F(PasswordManagerTest, HashSavedOnGaiaFormWithSkipSavePassword) {
+  EXPECT_CALL(client_, IsSavingAndFillingEnabledForCurrentPage())
+      .WillRepeatedly(Return(true));
+
+  std::vector<PasswordForm> observed;
+  PasswordForm form(MakeSimpleGAIAForm());
+  // Simulate that this is Gaia form that should be ignored for saving/filling.
+  form.is_gaia_with_skip_save_password_form = true;
+  observed.push_back(form);
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+  ON_CALL(*client_.GetStoreResultFilter(), ShouldSaveGaiaPasswordHash(_))
+      .WillByDefault(Return(true));
+  ON_CALL(*client_.GetStoreResultFilter(), ShouldSave(_))
+      .WillByDefault(Return(false));
+  ON_CALL(*client_.GetStoreResultFilter(), IsSyncAccountEmail(_))
+      .WillByDefault(Return(true));
+
+  EXPECT_CALL(*store_,
+              SaveGaiaPasswordHash(
+                  "googleuser", form.password_value,
+                  metrics_util::SyncPasswordHashChange::SAVED_IN_CONTENT_AREA));
+
+  OnPasswordFormSubmitted(form);
+  observed.clear();
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+#endif
+
 // On a successful login with an updated password,
 // CredentialsFilter::ReportFormLoginSuccess and CredentialsFilter::ShouldSave
 // should be called. The argument of ShouldSave shold be the submitted form.
diff --git a/components/policy/tools/schema_validator.py b/components/policy/tools/schema_validator.py
new file mode 100644
index 0000000..cf7e369
--- /dev/null
+++ b/components/policy/tools/schema_validator.py
@@ -0,0 +1,321 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import re
+
+# Dict with valid schema type names as keys. The values are the allowed
+# attribute names and their expected value types.
+#
+# These are the ONLY supported schema features. For the full schema proposal see
+# https://json-schema.org/understanding-json-schema/index.html.
+#
+# There are also these departures from the proposal:
+#   - "additionalProperties": false is not supported. Instead, it is assumed by
+#     default. The value of "additionalProperties" has to be a schema.
+ALLOWED_ATTRIBUTES_AND_TYPES = {
+    'boolean': {
+        'type': str,        # required
+        'id': str,          # optional
+        'description': str  # optional
+    },
+    'string': {
+        'type': str,        # required
+        'id': str,          # optional
+        'description': str, # optional
+        'enum': list,       # optional
+        'pattern': str      # optional
+    },
+    'integer': {
+        'type': str,        # required
+        'id': str,          # optional
+        'description': str, # optional
+        'enum': list,       # optional
+        'minimum': int,     # optional
+        'maximum': int      # optional
+    },
+    'array': {
+        'type': str,        # required
+        'id': str,          # optional
+        'description': str, # optional
+        'items': dict       # required
+    },
+    'object': {
+        'type': str,                  # required
+        'id': str,                    # optional
+        'description': str,           # optional
+        'properties': dict,           # only one of these
+        'patternProperties': dict,    #   three properties
+        'additionalProperties': dict, #   is required
+        'required': list              # optional
+    }
+}
+
+# Dict of allowed attributes and their expected types for schemas with a $ref.
+ALLOWED_REF_ATTRIBUTES_AND_TYPES = {'$ref': str, 'description': str}
+
+# Dict of human-readable enum types and their python types as values.
+ENUM_ITEM_TYPES = {'integer': int, 'string': str}
+
+
+class SchemaValidator(object):
+  """This class can validate schemas by calling ValidateSchema() or can
+  validate values against their schema by calling ValidateValue().
+  Schemas with an 'id' used as $ref link by another schema have to be passed to
+  ValidateSchema() before the schema with the $ref can be used in
+  ValidateSchema() or ValidateValue(schema, value).
+  |schemas_by_id| is used as storage for schemas with their 'id' as key and
+  their schemas as value.
+  """
+
+  def __init__(self):
+    self.schemas_by_id = {}
+    self.invalid_ref_ids = set()
+    self.found_ref_ids = set()
+    self.num_errors = 0
+
+  def ValidateSchema(self, schema):
+    """Checks if |schema| is a valid schema and only uses valid $ref links.
+
+    See _ValidateSchemaInternal() for a detailed description of the schema
+    validation. This method also checks if all used $ref links are known and
+    valid.
+    Args:
+      schema (dict): The JSON schema.
+    Returns:
+      bool: Whether the schema is valid or not.
+    """
+    self.found_ref_ids.clear()
+    self.num_errors = 0
+
+    self._ValidateSchemaInternal(schema)
+
+    unknown_ref_ids = self.found_ref_ids.difference(self.schemas_by_id.keys())
+    for unknown_ref_id in unknown_ref_ids:
+      if unknown_ref_id in self.invalid_ref_ids:
+        self._Error("$ref to invalid schema '%s'." % unknown_ref_id)
+      else:
+        self._Error("Unknown $ref '%s'." % unknown_ref_id)
+
+    return self.num_errors == 0
+
+  def _ValidateSchemaInternal(self, schema):
+    """Check if |schema| is a valid schema.
+
+    This method checks whether |schema| is a dict, has a valid 'type' property,
+    only has properties and values allowed by its type (see
+    ALLOWED_ATTRIBUTES_AND_TYPES) and calls the appropriate
+    _Validate{Integer,String,Array,Object}Schema() method.
+    If the schema has a $ref, the $ref id is added to |found_ref_ids| so that
+    its existence can be validated later on in ValidateSchema(). They may
+    also not contain other attributes except for '$ref' and 'description' (see
+    ALLOWED_REF_ATTRIBUTES_AND_TYPES).
+    If the schema has an id, the id has to be unique and the schema is stored
+    for later re-use if it is valid.
+    Args:
+      schema (dict): The JSON schema.
+    """
+    num_errors_before = self.num_errors
+    # Check that schema is of type dict.
+    if not isinstance(schema, dict):
+      self._Error("Schema must be a dict.")
+
+    # Validate $ref links. All '$ref' links are gathered in |found_ref_links| so
+    # that their existence can be validated later on in ValidateSchema(schema).
+    if '$ref' in schema:
+      ref_id = schema['$ref']
+      for name, value in schema.iteritems():
+        if name not in ALLOWED_REF_ATTRIBUTES_AND_TYPES:
+          self._Error("Attribute '%s' is not allowed for schema with $ref '%s'."
+                      % (name, ref_id))
+        expected_type = ALLOWED_REF_ATTRIBUTES_AND_TYPES[name]
+        if not isinstance(value, expected_type):
+          self._Error(
+              ("Attribute value for '%s' (%s) has incorrect type (Expected "
+               "type: '%s'; actual type: '%s')") % (name, value, expected_type,
+                                                    type(value)))
+      self.found_ref_ids.add(ref_id)
+      return
+
+    # Every schema (non-ref) must have a type.
+    if 'type' not in schema:
+      self._Error("Missing attribute 'type'.")
+    schema_type = schema['type']
+
+    # Check that the type is valid.
+    if schema_type not in ALLOWED_ATTRIBUTES_AND_TYPES:
+      self._Error("Unknown type: %s" % schema_type)
+
+    # Check that each schema only contains attributes that are allowed in their
+    # respective type and that their values are of correct type.
+    allowed_attributes = ALLOWED_ATTRIBUTES_AND_TYPES[schema_type]
+    for attribute_name, attribute_value in schema.iteritems():
+      if attribute_name in allowed_attributes:
+        expected_type = allowed_attributes[attribute_name]
+        if not isinstance(attribute_value, expected_type):
+          self._Error(
+              ("Attribute '%s' has incorrect type (Expected type: '%s'; actual "
+               "type: '%s').") % (attribute_name, expected_type,
+                                  type(attribute_value)))
+      else:
+        self._Error("Attribute '%s' is not allowed for type '%s'." %
+                    (attribute_name, schema_type))
+
+    # Validate schemas depending on 'type'.
+    if schema_type == 'string':
+      self._ValidateStringSchema(schema)
+    elif schema_type == 'integer':
+      self._ValidateIntegerSchema(schema)
+    elif schema_type == 'array':
+      self._ValidateArraySchema(schema)
+    elif schema_type == 'object':
+      self._ValidateObjectSchema(schema)
+
+    # If the schema has an 'id', ensure that the id is unique and store the
+    # schema for later reference.
+    if 'id' in schema:
+      ref_id = schema['id']
+      if ref_id in self.schemas_by_id:
+        self._Error("ID '%s' is not unique." % ref_id)
+      if self.num_errors == num_errors_before:
+        self.schemas_by_id[ref_id] = schema
+      else:
+        self.invalid_ref_ids.add(ref_id)
+
+  def _ValidateStringSchema(self, schema):
+    """Validates a |schema| with type 'string'.
+
+    Validates the 'enum' (see _ValidateEnum()) and/or 'pattern' property (see
+    _ValidatePattern()) if existing.
+    Args:
+      schema (dict): The JSON schema.
+    """
+    if 'enum' in schema:
+      self._ValidateEnum(schema['enum'], 'string')
+    if 'pattern' in schema:
+      self._ValidatePattern(schema['pattern'])
+
+  def _ValidateIntegerSchema(self, schema):
+    """Validates a |schema| with type 'integer'.
+
+    Validates the 'enum' property (see _ValidateEnum()) if existing. This
+    method also ensures that the specified minimum value is smaller or equal to
+    the specified maximum value, if both exist.
+    Args:
+      schema (dict): The JSON schema.
+    """
+    if 'enum' in schema:
+      self._ValidateEnum(schema['enum'], 'integer')
+    if ('minimum' in schema and 'maximum' in schema and
+        schema['minimum'] > schema['maximum']):
+      self._Error("Invalid range specified: [%s; %s]" % (schema['minimum'],
+                                                         schema['maximum']))
+
+  def _ValidateArraySchema(self, schema):
+    """Validates a |schema| with type 'array'.
+
+    Validates that the 'items' attribute exists and its value is a valid schema.
+    Args:
+      schema (dict): The JSON schema.
+    """
+    if 'items' in schema:
+      self._ValidateSchemaInternal(schema['items'])
+    else:
+      self._Error("Schema of type 'array' must have an 'items' attribute.")
+
+  def _ValidateObjectSchema(self, schema):
+    """Validates a schema of type 'object'.
+
+    If |schema| has a 'required' attribute, this method validates that it is not
+    empty, only contains strings and only contains property names of properties
+    defined in the 'properties' attribute.
+    This method also ensures that at least one of 'properties',
+    'patternProperties' or 'additionalProperties' is defined.
+    If 'properties' are defined, they must have non-empty string names and
+    contain a valid schema.
+    If 'patternProperties' are defined, they must be valid regex patterns and
+    contain a valid schema.
+    If 'additionalProperties is defined, it must contain a valid schema.
+    Args:
+      schema (dict): The JSON schema.
+    '"""
+    # Validate 'required' attribute.
+    if 'required' in schema:
+      required_properties = schema['required']
+      if not required_properties:
+        self._Error("Attribute 'required' may not be empty (omit it if empty).")
+      if not all(
+          isinstance(required_property, str)
+          for required_property in required_properties):
+        self._Error("Attribute 'required' may only contain strings.")
+      properties = schema.get('properties', {})
+      unknown_properties = [
+          property_name for property_name in required_properties
+          if property_name not in properties
+      ]
+      if unknown_properties:
+        self._Error("Unknown properties in 'required': %s" % unknown_properties)
+    # Validate '*properties' attributes.
+    has_any_properties = False
+    if 'properties' in schema:
+      has_any_properties = True
+      properties = schema['properties']
+      for property_name, property_schema in properties.iteritems():
+        if not isinstance(property_name, str):
+          self._Error("Property name must be a string.")
+        if not property_name:
+          self._Error("Property name may not be empty.")
+        self._ValidateSchemaInternal(property_schema)
+    if 'patternProperties' in schema:
+      has_any_properties = True
+      pattern_properties = schema['patternProperties']
+      for property_pattern, property_schema in pattern_properties.iteritems():
+        self._ValidatePattern(property_pattern)
+        self._ValidateSchemaInternal(property_schema)
+    if 'additionalProperties' in schema:
+      has_any_properties = True
+      additional_properties = schema['additionalProperties']
+      self._ValidateSchemaInternal(additional_properties)
+    if not has_any_properties:
+      self._Error(
+          "Schema of type 'object' must have at least one of the following "
+          "attributes: ['properties', 'patternProperties' or "
+          "'additionalProperties'].")
+
+  def _ValidateEnum(self, enum, schema_type):
+    """Validates an |enum| of type |schema_type|.
+
+    Validates that |enum| is not empty and its elements have the correct type
+    according to |schema_type| (see ENUM_ITEM_TYPES).
+    Args:
+      enum (list): The list of enum values.
+      schema_type (str): The schema type in which the enum is used.
+    """
+    if not enum:
+      self._Error("Attribute 'enum' may not be empty.")
+    item_type = ENUM_ITEM_TYPES[schema_type]
+    if not all(isinstance(enum_value, item_type) for enum_value in enum):
+      self._Error(("Attribute 'enum' for type '%s' may only contain elements of"
+                   " type %s: %s") % (schema_type, item_type, enum))
+
+  def _ValidatePattern(self, pattern):
+    """Validates a regex |pattern|.
+
+    Validates that |pattern| is a string and can be used as regex pattern.
+    Args:
+      pattern (str): The regex pattern.
+    """
+    if not isinstance(pattern, str):
+      self._Error("Pattern must be a string: %s" % pattern)
+    try:
+      re.compile(pattern)
+    except re.error:
+      self._Error("Pattern is not a valid regex: %s" % pattern)
+
+  def _Error(self, message):
+    """Captures an error.
+
+    Logs the error |message| and increases the error count |num_errors| by one.
+    Args:
+      message (str): The error message."""
+    self.num_errors += 1
+    print(message)
\ No newline at end of file
diff --git a/components/policy/tools/syntax_check_policy_template_json.py b/components/policy/tools/syntax_check_policy_template_json.py
index 43ce4c3..d61c00bb 100755
--- a/components/policy/tools/syntax_check_policy_template_json.py
+++ b/components/policy/tools/syntax_check_policy_template_json.py
@@ -12,6 +12,7 @@
 import os
 import re
 import sys
+from schema_validator import SchemaValidator
 
 
 LEADING_WHITESPACE = re.compile('^([ \t]*)')
@@ -79,6 +80,7 @@
     self.num_policies_in_groups = 0
     self.options = None
     self.features = []
+    self.schema_validator = SchemaValidator()
 
   def _Error(self, message, parent_element=None, identifier=None,
              offending_snippet=None):
@@ -186,39 +188,46 @@
 
   def _CheckPolicySchema(self, policy, policy_type):
     '''Checks that the 'schema' field matches the 'type' field.'''
-    self._CheckContains(policy, 'schema', dict)
-    if isinstance(policy.get('schema'), dict):
-      self._CheckContains(policy['schema'], 'type', str)
-      schema_type = policy['schema'].get('type')
+    schema = self._CheckContains(policy, 'schema', dict)
+    if schema:
+      schema_type = self._CheckContains(schema, 'type', str)
       if schema_type not in TYPE_TO_SCHEMA[policy_type]:
         self._Error('Schema type must match the existing type for policy %s' %
                     policy.get('name'))
+      if not self.schema_validator.ValidateSchema(schema):
+        self._Error('Schema is invalid for policy %s' % policy.get('name'))
 
-      # Checks that boolean policies are not negated (which makes them harder to
-      # reason about).
-      if (schema_type == 'boolean' and
-          'disable' in policy.get('name').lower() and
-          policy.get('name') not in LEGACY_INVERTED_POLARITY_WHITELIST):
-        self._Error(('Boolean policy %s uses negative polarity, please make ' +
-                     'new boolean policies follow the XYZEnabled pattern. ' +
-                     'See also http://crbug.com/85687') % policy.get('name'))
+    if policy.has_key('validation_schema'):
+      validation_schema = policy.get('validation_schema')
+      if not self.schema_validator.ValidateSchema(validation_schema):
+        self._Error('Validation schema is invalid for policy %s' %
+                    policy.get('name'))
 
-      # Checks that the policy doesn't have a validation_schema - the whole
-      # schema should be defined in 'schema'- unless whitelisted as legacy.
-      if (policy.has_key('validation_schema') and
-          policy.get('name') not in LEGACY_EMBEDDED_JSON_WHITELIST):
-        self._Error(('"validation_schema" is defined for new policy %s - ' +
-                     'entire schema data should be contained in "schema"') %
-                     policy.get('name'))
+    # Checks that boolean policies are not negated (which makes them harder to
+    # reason about).
+    if (policy_type == 'main' and
+        'disable' in policy.get('name').lower() and
+        policy.get('name') not in LEGACY_INVERTED_POLARITY_WHITELIST):
+      self._Error(('Boolean policy %s uses negative polarity, please make ' +
+                    'new boolean policies follow the XYZEnabled pattern. ' +
+                    'See also http://crbug.com/85687') % policy.get('name'))
 
-      # Try to make sure that any policy with a complex schema is storing it as
-      # a 'dict', not embedding it inside JSON strings - unless whitelisted.
-      if (self._AppearsToContainEmbeddedJson(policy.get('example_value')) and
-          policy.get('name') not in LEGACY_EMBEDDED_JSON_WHITELIST):
-        self._Error(('Example value for new policy %s looks like JSON. Do ' +
-                     'not store complex data as stringified JSON - instead, ' +
-                     'store it in a dict and define it in "schema".') %
-                     policy.get('name'))
+    # Checks that the policy doesn't have a validation_schema - the whole
+    # schema should be defined in 'schema'- unless whitelisted as legacy.
+    if (policy.has_key('validation_schema') and
+        policy.get('name') not in LEGACY_EMBEDDED_JSON_WHITELIST):
+      self._Error(('"validation_schema" is defined for new policy %s - ' +
+                    'entire schema data should be contained in "schema"') %
+                    policy.get('name'))
+
+    # Try to make sure that any policy with a complex schema is storing it as
+    # a 'dict', not embedding it inside JSON strings - unless whitelisted.
+    if (self._AppearsToContainEmbeddedJson(policy.get('example_value')) and
+        policy.get('name') not in LEGACY_EMBEDDED_JSON_WHITELIST):
+      self._Error(('Example value for new policy %s looks like JSON. Do ' +
+                    'not store complex data as stringified JSON - instead, ' +
+                    'store it in a dict and define it in "schema".') %
+                    policy.get('name'))
 
   # Returns True if the example value for a policy seems to contain JSON
   # embedded inside a string. Simply checks if strings start with '{', so it
diff --git a/components/printing/browser/print_composite_client.cc b/components/printing/browser/print_composite_client.cc
index bc2a680..c47030b9 100644
--- a/components/printing/browser/print_composite_client.cc
+++ b/components/printing/browser/print_composite_client.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "components/printing/common/print_messages.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
@@ -19,15 +20,45 @@
 #include "printing/printing_utils.h"
 #include "services/service_manager/public/cpp/connector.h"
 
+namespace printing {
+
 namespace {
 
-uint64_t GenerateFrameGuid(int process_id, int frame_id) {
+uint64_t GenerateFrameGuid(content::RenderFrameHost* render_frame_host) {
+  int process_id = render_frame_host->GetProcess()->GetID();
+  int frame_id = render_frame_host->GetRoutingID();
   return static_cast<uint64_t>(process_id) << 32 | frame_id;
 }
 
-}  // namespace
+// Converts a ContentToProxyIdMap to ContentToFrameMap.
+// ContentToProxyIdMap maps content id to the routing id of its corresponding
+// render frame proxy. This is generated when the content holder was created;
+// ContentToFrameMap maps content id to its render frame's global unique id.
+// The global unique id has the render process id concatenated with render
+// frame routing id, which can uniquely identify a render frame.
+ContentToFrameMap ConvertContentInfoMap(
+    content::RenderFrameHost* render_frame_host,
+    const ContentToProxyIdMap& content_proxy_map) {
+  ContentToFrameMap content_frame_map;
+  int process_id = render_frame_host->GetProcess()->GetID();
+  for (const auto& entry : content_proxy_map) {
+    auto content_id = entry.first;
+    auto proxy_id = entry.second;
+    // Find the RenderFrameHost that the proxy id corresponds to.
+    content::RenderFrameHost* rfh =
+        content::RenderFrameHost::FromPlaceholderId(process_id, proxy_id);
+    if (!rfh) {
+      // If the corresponding RenderFrameHost cannot be found, just skip it.
+      continue;
+    }
 
-namespace printing {
+    // Store this frame's global unique id into the map.
+    content_frame_map[content_id] = GenerateFrameGuid(rfh);
+  }
+  return content_frame_map;
+}
+
+}  // namespace
 
 PrintCompositeClient::PrintCompositeClient(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents) {}
@@ -49,13 +80,12 @@
 
 void PrintCompositeClient::RenderFrameDeleted(
     content::RenderFrameHost* render_frame_host) {
-  auto frame_guid = GenerateFrameGuid(render_frame_host->GetProcess()->GetID(),
-                                      render_frame_host->GetRoutingID());
+  uint64_t frame_guid = GenerateFrameGuid(render_frame_host);
   auto iter = pending_subframe_cookies_.find(frame_guid);
   if (iter != pending_subframe_cookies_.end()) {
     // When a subframe we are expecting is deleted, we should notify pdf
     // compositor service.
-    for (auto doc_cookie : iter->second) {
+    for (int doc_cookie : iter->second) {
       auto& compositor = GetCompositeRequest(doc_cookie);
       compositor->NotifyUnavailableSubframe(frame_guid);
     }
@@ -90,12 +120,10 @@
   mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
       params.metafile_data_handle, params.data_size,
       mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly);
-  auto frame_guid = GenerateFrameGuid(render_frame_host->GetProcess()->GetID(),
-                                      render_frame_host->GetRoutingID());
+  uint64_t frame_guid = GenerateFrameGuid(render_frame_host);
   compositor->AddSubframeContent(
       frame_guid, std::move(buffer_handle),
-      ConvertContentInfoMap(web_contents(), render_frame_host,
-                            params.subframe_content_info));
+      ConvertContentInfoMap(render_frame_host, params.subframe_content_info));
 
   // Update our internal states about this frame.
   pending_subframe_cookies_[frame_guid].erase(document_cookie);
@@ -111,109 +139,97 @@
   PrintMsg_PrintFrame_Params params;
   params.printable_area = rect;
   params.document_cookie = document_cookie;
-  uint64_t frame_guid = GenerateFrameGuid(subframe_host->GetProcess()->GetID(),
-                                          subframe_host->GetRoutingID());
-  if (subframe_host->IsRenderFrameLive()) {
-    auto subframe_iter = printed_subframes_.find(document_cookie);
-    if (subframe_iter != printed_subframes_.end() &&
-        base::ContainsKey(subframe_iter->second, frame_guid)) {
-      // If this frame is already printed, no need to print again.
-      return;
-    }
-
-    auto cookie_iter = pending_subframe_cookies_.find(frame_guid);
-    if (cookie_iter != pending_subframe_cookies_.end() &&
-        base::ContainsKey(cookie_iter->second, document_cookie)) {
-      // If this frame is being printed, no need to print again.
-      return;
-    }
-
-    // Send the request to the destination frame.
-    subframe_host->Send(
-        new PrintMsg_PrintFrameContent(subframe_host->GetRoutingID(), params));
-    pending_subframe_cookies_[frame_guid].insert(document_cookie);
-  } else {
+  uint64_t frame_guid = GenerateFrameGuid(subframe_host);
+  if (!subframe_host->IsRenderFrameLive()) {
     // When the subframe is dead, no need to send message,
     // just notify the service.
     auto& compositor = GetCompositeRequest(document_cookie);
     compositor->NotifyUnavailableSubframe(frame_guid);
+    return;
   }
+
+  auto subframe_iter = printed_subframes_.find(document_cookie);
+  if (subframe_iter != printed_subframes_.end() &&
+      base::ContainsKey(subframe_iter->second, frame_guid)) {
+    // If this frame is already printed, no need to print again.
+    return;
+  }
+
+  auto cookie_iter = pending_subframe_cookies_.find(frame_guid);
+  if (cookie_iter != pending_subframe_cookies_.end() &&
+      base::ContainsKey(cookie_iter->second, document_cookie)) {
+    // If this frame is being printed, no need to print again.
+    return;
+  }
+
+  // Send the request to the destination frame.
+  subframe_host->Send(
+      new PrintMsg_PrintFrameContent(subframe_host->GetRoutingID(), params));
+  pending_subframe_cookies_[frame_guid].insert(document_cookie);
 }
 
 void PrintCompositeClient::DoCompositePageToPdf(
     int document_cookie,
     content::RenderFrameHost* render_frame_host,
     int page_num,
-    base::SharedMemoryHandle handle,
-    uint32_t data_size,
-    const ContentToProxyIdMap& subframe_content_info,
+    const PrintHostMsg_DidPrintContent_Params& content,
     mojom::PdfCompositor::CompositePageToPdfCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   auto& compositor = GetCompositeRequest(document_cookie);
 
-  DCHECK(data_size);
+  DCHECK(content.data_size);
   mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
-      handle, data_size,
+      content.metafile_data_handle, content.data_size,
       mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly);
-  // Since this class owns compositor, compositor will be gone when this class
-  // is destructed. Mojo won't call its callback in that case so it is safe to
-  // use unretained |this| pointer here.
   compositor->CompositePageToPdf(
-      GenerateFrameGuid(render_frame_host->GetProcess()->GetID(),
-                        render_frame_host->GetRoutingID()),
-      page_num, std::move(buffer_handle),
-      ConvertContentInfoMap(web_contents(), render_frame_host,
-                            subframe_content_info),
+      GenerateFrameGuid(render_frame_host), page_num, std::move(buffer_handle),
+      ConvertContentInfoMap(render_frame_host, content.subframe_content_info),
       base::BindOnce(&PrintCompositeClient::OnDidCompositePageToPdf,
-                     base::Unretained(this), std::move(callback)));
+                     std::move(callback)));
 }
 
 void PrintCompositeClient::DoCompositeDocumentToPdf(
     int document_cookie,
     content::RenderFrameHost* render_frame_host,
-    base::SharedMemoryHandle handle,
-    uint32_t data_size,
-    const ContentToProxyIdMap& subframe_content_info,
+    const PrintHostMsg_DidPrintContent_Params& content,
     mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   auto& compositor = GetCompositeRequest(document_cookie);
 
-  DCHECK(data_size);
+  DCHECK(content.data_size);
   mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
-      handle, data_size,
+      content.metafile_data_handle, content.data_size,
       mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly);
+
   // Since this class owns compositor, compositor will be gone when this class
   // is destructed. Mojo won't call its callback in that case so it is safe to
   // use unretained |this| pointer here.
   compositor->CompositeDocumentToPdf(
-      GenerateFrameGuid(render_frame_host->GetProcess()->GetID(),
-                        render_frame_host->GetRoutingID()),
-      std::move(buffer_handle),
-      ConvertContentInfoMap(web_contents(), render_frame_host,
-                            subframe_content_info),
+      GenerateFrameGuid(render_frame_host), std::move(buffer_handle),
+      ConvertContentInfoMap(render_frame_host, content.subframe_content_info),
       base::BindOnce(&PrintCompositeClient::OnDidCompositeDocumentToPdf,
                      base::Unretained(this), document_cookie,
                      std::move(callback)));
 }
 
+// static
 void PrintCompositeClient::OnDidCompositePageToPdf(
-    printing::mojom::PdfCompositor::CompositePageToPdfCallback callback,
-    printing::mojom::PdfCompositor::Status status,
+    mojom::PdfCompositor::CompositePageToPdfCallback callback,
+    mojom::PdfCompositor::Status status,
     base::ReadOnlySharedMemoryRegion region) {
   // Due to https://crbug.com/742517, we can not add and use COUNT for enums in
   // mojo.
   UMA_HISTOGRAM_ENUMERATION(
       "CompositePageToPdf.Status", status,
-      static_cast<int32_t>(
-          printing::mojom::PdfCompositor::Status::COMPOSTING_FAILURE) +
+      static_cast<int32_t>(mojom::PdfCompositor::Status::COMPOSTING_FAILURE) +
           1);
   std::move(callback).Run(status, std::move(region));
 }
 
 void PrintCompositeClient::OnDidCompositeDocumentToPdf(
     int document_cookie,
-    printing::mojom::PdfCompositor::CompositeDocumentToPdfCallback callback,
-    printing::mojom::PdfCompositor::Status status,
+    mojom::PdfCompositor::CompositeDocumentToPdfCallback callback,
+    mojom::PdfCompositor::Status status,
     base::ReadOnlySharedMemoryRegion region) {
   RemoveCompositeRequest(document_cookie);
   // Clear all stored printed subframes.
@@ -223,37 +239,11 @@
   // mojo.
   UMA_HISTOGRAM_ENUMERATION(
       "CompositeDocToPdf.Status", status,
-      static_cast<int32_t>(
-          printing::mojom::PdfCompositor::Status::COMPOSTING_FAILURE) +
+      static_cast<int32_t>(mojom::PdfCompositor::Status::COMPOSTING_FAILURE) +
           1);
   std::move(callback).Run(status, std::move(region));
 }
 
-ContentToFrameMap PrintCompositeClient::ConvertContentInfoMap(
-    content::WebContents* web_contents,
-    content::RenderFrameHost* render_frame_host,
-    const ContentToProxyIdMap& content_proxy_map) {
-  ContentToFrameMap content_frame_map;
-  int process_id = render_frame_host->GetProcess()->GetID();
-  for (auto& entry : content_proxy_map) {
-    auto content_id = entry.first;
-    auto proxy_id = entry.second;
-    // Find the RenderFrameHost that the proxy id corresponds to.
-    content::RenderFrameHost* rfh =
-        content::RenderFrameHost::FromPlaceholderId(process_id, proxy_id);
-    if (!rfh) {
-      // If we could not find the corresponding RenderFrameHost,
-      // just skip it.
-      continue;
-    }
-
-    // Store this frame's global unique id into the map.
-    content_frame_map[content_id] =
-        GenerateFrameGuid(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
-  }
-  return content_frame_map;
-}
-
 mojom::PdfCompositorPtr& PrintCompositeClient::GetCompositeRequest(int cookie) {
   auto iter = compositor_map_.find(cookie);
   if (iter != compositor_map_.end()) {
diff --git a/components/printing/browser/print_composite_client.h b/components/printing/browser/print_composite_client.h
index 8718f321..36c5fb1 100644
--- a/components/printing/browser/print_composite_client.h
+++ b/components/printing/browser/print_composite_client.h
@@ -9,16 +9,16 @@
 #include <memory>
 
 #include "base/containers/flat_set.h"
-#include "base/optional.h"
-#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
 #include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
-#include "printing/common/pdf_metafile_utils.h"
-#include "services/service_manager/public/cpp/connector.h"
 
 struct PrintHostMsg_DidPrintContent_Params;
 
+namespace service_manager {
+class Connector;
+}
+
 namespace printing {
 
 // Class to manage print requests and their communication with pdf
@@ -49,8 +49,9 @@
                                  int document_cookie,
                                  content::RenderFrameHost* subframe_host);
 
-  // NOTE: |handle| must be a READ-ONLY base::SharedMemoryHandle, i.e. one
-  // acquired by base::SharedMemory::GetReadOnlyHandle().
+  // NOTE: |content.metafile_data_handle| must be a READ-ONLY
+  // base::SharedMemoryHandle, i.e. one acquired by
+  // base::SharedMemory::GetReadOnlyHandle().
 
   // Printing single pages is only used by print preview for early return of
   // rendered results. In this case, the pages share the content with printed
@@ -60,9 +61,7 @@
       int cookie,
       content::RenderFrameHost* render_frame_host,
       int page_num,
-      base::SharedMemoryHandle handle,
-      uint32_t data_size,
-      const ContentToProxyIdMap& subframe_content_info,
+      const PrintHostMsg_DidPrintContent_Params& content,
       mojom::PdfCompositor::CompositePageToPdfCallback callback);
 
   // Used for compositing the entire document for print preview or actual
@@ -70,37 +69,20 @@
   void DoCompositeDocumentToPdf(
       int cookie,
       content::RenderFrameHost* render_frame_host,
-      base::SharedMemoryHandle handle,
-      uint32_t data_size,
-      const ContentToProxyIdMap& subframe_content_info,
+      const PrintHostMsg_DidPrintContent_Params& content,
       mojom::PdfCompositor::CompositeDocumentToPdfCallback callback);
 
-  // Converts a ContentToProxyIdMap to ContentToFrameMap.
-  // ContentToProxyIdMap maps content id to its corresponding render frame proxy
-  // routing id. This is generated when the content holder was created;
-  // ContentToFrameMap maps content id to its render frame's global unique id.
-  // The global unique id has the render process id concatenated with render
-  // frame routing id, which can uniquely identify a render frame.
-  static ContentToFrameMap ConvertContentInfoMap(
-      content::WebContents* web_contents,
-      content::RenderFrameHost* render_frame_host,
-      const ContentToProxyIdMap& content_proxy_map);
-
  private:
-  // Since page number is always non-negative, use this value to indicate it is
-  // for the whole document -- no page number specified.
-  static constexpr int kPageNumForWholeDoc = -1;
-
   // Callback functions for getting the replies.
-  void OnDidCompositePageToPdf(
-      printing::mojom::PdfCompositor::CompositePageToPdfCallback callback,
-      printing::mojom::PdfCompositor::Status status,
+  static void OnDidCompositePageToPdf(
+      mojom::PdfCompositor::CompositePageToPdfCallback callback,
+      mojom::PdfCompositor::Status status,
       base::ReadOnlySharedMemoryRegion region);
 
   void OnDidCompositeDocumentToPdf(
       int document_cookie,
-      printing::mojom::PdfCompositor::CompositeDocumentToPdfCallback callback,
-      printing::mojom::PdfCompositor::Status status,
+      mojom::PdfCompositor::CompositeDocumentToPdfCallback callback,
+      mojom::PdfCompositor::Status status,
       base::ReadOnlySharedMemoryRegion region);
 
   // Get the request or create a new one if none exists.
diff --git a/components/translate/ios/browser/js_translate_manager_unittest.mm b/components/translate/ios/browser/js_translate_manager_unittest.mm
index d559701..590d58e 100644
--- a/components/translate/ios/browser/js_translate_manager_unittest.mm
+++ b/components/translate/ios/browser/js_translate_manager_unittest.mm
@@ -23,7 +23,7 @@
 @implementation JsTranslateManager (Testing)
 // Returns the time in milliseconds.
 - (double)performanceNow {
-  id result = web::ExecuteJavaScript(self.receiver, @"performance.now()");
+  id result = web::test::ExecuteJavaScript(self.receiver, @"performance.now()");
   return [result doubleValue];
 }
 @end
@@ -43,7 +43,7 @@
   bool IsDefined(NSString* name) {
     NSString* script =
         [NSString stringWithFormat:@"typeof %@ != 'undefined'", name];
-    return [web::ExecuteJavaScript(receiver_, script) boolValue];
+    return [web::test::ExecuteJavaScript(receiver_, script) boolValue];
   }
 
   CRWTestJSInjectionReceiver* receiver_;
@@ -80,6 +80,6 @@
   [manager_ inject];
   EXPECT_TRUE([manager_ hasBeenInjected]);
   EXPECT_EQ(nil, [manager_ script]);
-  EXPECT_NSEQ(@NO,
-              web::ExecuteJavaScript(manager_, @"cr.googleTranslate.libReady"));
+  EXPECT_NSEQ(@NO, web::test::ExecuteJavaScript(
+                       manager_, @"cr.googleTranslate.libReady"));
 }
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 898af5d..913ce7f49 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1426,6 +1426,21 @@
   RunHtmlTest(FILE_PATH_LITERAL("input-search.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityScrollableInput) {
+  RunHtmlTest(FILE_PATH_LITERAL("scrollable-input.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityScrollableOverflow) {
+  RunHtmlTest(FILE_PATH_LITERAL("scrollable-overflow.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityScrollableTextarea) {
+  RunHtmlTest(FILE_PATH_LITERAL("scrollable-textarea.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySmall) {
   RunHtmlTest(FILE_PATH_LITERAL("small.html"));
 }
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index 2b27d83..d92c8dc 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -24,29 +24,31 @@
 
 namespace {
 
-void FillCacheInfo(const AppCache* cache,
-                   const GURL& manifest_url,
-                   AppCacheStatus status, AppCacheInfo* info) {
-  info->manifest_url = manifest_url;
-  info->status = status;
+AppCacheInfo CreateCacheInfo(const AppCache* cache,
+                             const GURL& manifest_url,
+                             AppCacheStatus status) {
+  AppCacheInfo info;
+  info.manifest_url = manifest_url;
+  info.status = status;
 
   if (!cache)
-    return;
+    return info;
 
-  info->cache_id = cache->cache_id();
+  info.cache_id = cache->cache_id();
 
   if (!cache->is_complete())
-    return;
+    return info;
 
   DCHECK(cache->owning_group());
-  info->is_complete = true;
-  info->group_id = cache->owning_group()->group_id();
-  info->last_update_time = cache->update_time();
-  info->creation_time = cache->owning_group()->creation_time();
-  info->size = cache->cache_size();
+  info.is_complete = true;
+  info.group_id = cache->owning_group()->group_id();
+  info.last_update_time = cache->update_time();
+  info.creation_time = cache->owning_group()->creation_time();
+  info.size = cache->cache_size();
+  return info;
 }
 
-}  // Anonymous namespace
+}  // namespace
 
 AppCacheHost::AppCacheHost(int host_id,
                            AppCacheFrontend* frontend,
@@ -463,9 +465,8 @@
 
   if (associated_cache_info_pending_ && associated_cache_.get() &&
       associated_cache_->is_complete()) {
-    AppCacheInfo info;
-    FillCacheInfo(
-        associated_cache_.get(), preferred_manifest_url_, GetStatus(), &info);
+    AppCacheInfo info = CreateCacheInfo(associated_cache_.get(),
+                                        preferred_manifest_url_, GetStatus());
     associated_cache_info_pending_ = false;
     // In the network service world, we need to pass the URLLoaderFactory
     // instance to the renderer which it can use to request subresources.
@@ -562,11 +563,10 @@
   associated_cache_ = cache;
   SetSwappableCache(cache ? cache->owning_group() : nullptr);
   associated_cache_info_pending_ = cache && !cache->is_complete();
-  AppCacheInfo info;
   if (cache)
     cache->AssociateHost(this);
 
-  FillCacheInfo(cache, manifest_url, GetStatus(), &info);
+  AppCacheInfo info = CreateCacheInfo(cache, manifest_url, GetStatus());
   // In the network service world, we need to pass the URLLoaderFactory
   // instance to the renderer which it can use to request subresources.
   // This ensures that they can be served out of the AppCache.
diff --git a/content/browser/code_cache/generated_code_cache.h b/content/browser/code_cache/generated_code_cache.h
index 29165790..58e735b44 100644
--- a/content/browser/code_cache/generated_code_cache.h
+++ b/content/browser/code_cache/generated_code_cache.h
@@ -37,6 +37,8 @@
   static const int kResponseTimeSizeInBytes = sizeof(int64_t);
 
   // Creates a GeneratedCodeCache with the specified path and the maximum size.
+  // If |max_size_bytes| is 0, then disk_cache picks a default size based on
+  // some heuristics.
   GeneratedCodeCache(const base::FilePath& path, int max_size_bytes);
 
   ~GeneratedCodeCache();
diff --git a/content/browser/compositor/image_transport_factory_browsertest.cc b/content/browser/compositor/image_transport_factory_browsertest.cc
index 91e6d9e..95f86bfa 100644
--- a/content/browser/compositor/image_transport_factory_browsertest.cc
+++ b/content/browser/compositor/image_transport_factory_browsertest.cc
@@ -6,9 +6,7 @@
 
 #include "base/run_loop.h"
 #include "build/build_config.h"
-#include "components/viz/common/features.h"
 #include "components/viz/common/gpu/context_provider.h"
-#include "content/browser/compositor/owned_mailbox.h"
 #include "content/public/test/content_browser_test.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
@@ -63,43 +61,5 @@
   factory->GetContextFactory()->RemoveObserver(&observer);
 }
 
-class ImageTransportFactoryTearDownBrowserTest : public ContentBrowserTest {
- public:
-  void TearDown() override {
-    // Mailbox is null if the test exited early.
-    if (mailbox_.get())
-      EXPECT_TRUE(mailbox_->mailbox().IsZero());
-    ContentBrowserTest::TearDown();
-  }
-
- protected:
-  scoped_refptr<OwnedMailbox> mailbox_;
-};
-
-// Checks that upon destruction of the ImageTransportFactory, the observer is
-// called and the created resources are reset.
-IN_PROC_BROWSER_TEST_F(ImageTransportFactoryTearDownBrowserTest,
-                       LoseOnTearDown) {
-  ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-
-  // TODO(crbug.com/844469): Delete after OOP-D is launched because OwnedMailbox
-  // isn't used.
-  if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor))
-    return;
-
-  // This test doesn't make sense in software compositing mode.
-  if (factory->IsGpuCompositingDisabled())
-    return;
-
-  gpu::gles2::GLES2Interface* const gl = factory->GetContextFactory()
-                                             ->SharedMainThreadContextProvider()
-                                             ->ContextGL();
-  ASSERT_TRUE(gl);
-  mailbox_ = base::MakeRefCounted<OwnedMailbox>(gl);
-  EXPECT_FALSE(mailbox_->mailbox().IsZero());
-
-  // See TearDown() for the test expectation that |mailbox_| has been reset.
-}
-
 }  // anonymous namespace
 }  // namespace content
diff --git a/content/browser/compositor/owned_mailbox.cc b/content/browser/compositor/owned_mailbox.cc
index d1509b6c..0969eca 100644
--- a/content/browser/compositor/owned_mailbox.cc
+++ b/content/browser/compositor/owned_mailbox.cc
@@ -4,13 +4,13 @@
 
 #include "content/browser/compositor/owned_mailbox.h"
 
-#include "content/browser/compositor/image_transport_factory.h"
+#include "base/bind.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 
 namespace content {
 
 OwnedMailbox::OwnedMailbox(gpu::gles2::GLES2Interface* gl)
-    : gl_(gl), texture_id_(0) {
+    : gl_(gl), texture_id_(0), weak_ptr_factory_(this) {
   DCHECK(gl_);
 
   // Create the texture.
@@ -28,36 +28,23 @@
   gl_->ProduceTextureDirectCHROMIUM(texture_id_, mailbox_holder_.mailbox.name);
   gl_->GenSyncTokenCHROMIUM(mailbox_holder_.sync_token.GetData());
   mailbox_holder_.texture_target = GL_TEXTURE_2D;
-
-  ImageTransportFactory::GetInstance()->GetContextFactory()->AddObserver(this);
 }
 
 OwnedMailbox::~OwnedMailbox() {
-  Destroy();
+  gl_->WaitSyncTokenCHROMIUM(mailbox_holder_.sync_token.GetConstData());
+  gl_->DeleteTextures(1, &texture_id_);
 }
 
-void OwnedMailbox::UpdateSyncToken(const gpu::SyncToken& sync_token) {
+std::unique_ptr<viz::SingleReleaseCallback>
+OwnedMailbox::GetSingleReleaseCallback() {
+  return viz::SingleReleaseCallback::Create(base::BindOnce(
+      &OwnedMailbox::UpdateSyncToken, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void OwnedMailbox::UpdateSyncToken(const gpu::SyncToken& sync_token,
+                                   bool is_lost) {
   if (sync_token.HasData())
     mailbox_holder_.sync_token = sync_token;
 }
 
-void OwnedMailbox::Destroy() {
-  if (texture_id_ == 0)
-    return;
-
-  ImageTransportFactory::GetInstance()->GetContextFactory()->RemoveObserver(
-      this);
-
-  gl_->WaitSyncTokenCHROMIUM(mailbox_holder_.sync_token.GetConstData());
-  gl_->DeleteTextures(1, &texture_id_);
-  texture_id_ = 0;
-  mailbox_holder_ = gpu::MailboxHolder();
-}
-
-void OwnedMailbox::OnLostSharedContext() {
-  Destroy();
-}
-
-void OwnedMailbox::OnLostVizProcess() {}
-
 }  // namespace content
diff --git a/content/browser/compositor/owned_mailbox.h b/content/browser/compositor/owned_mailbox.h
index af3fa1a9..084e28f 100644
--- a/content/browser/compositor/owned_mailbox.h
+++ b/content/browser/compositor/owned_mailbox.h
@@ -7,8 +7,11 @@
 
 #include <stdint.h>
 
+#include <memory>
+
 #include "base/memory/ref_counted.h"
-#include "content/browser/compositor/image_transport_factory.h"
+#include "base/memory/weak_ptr.h"
+#include "components/viz/common/resources/single_release_callback.h"
 #include "content/common/content_export.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "ui/compositor/compositor.h"
@@ -23,8 +26,7 @@
 
 // This class holds a texture id and gpu::Mailbox, and deletes the texture
 // id when the object itself is destroyed.
-class CONTENT_EXPORT OwnedMailbox : public base::RefCounted<OwnedMailbox>,
-                                    public ui::ContextFactoryObserver {
+class CONTENT_EXPORT OwnedMailbox : public base::RefCounted<OwnedMailbox> {
  public:
   explicit OwnedMailbox(gpu::gles2::GLES2Interface* gl);
 
@@ -34,22 +36,21 @@
     return mailbox_holder_.sync_token;
   }
   uint32_t texture_id() const { return texture_id_; }
-  void UpdateSyncToken(const gpu::SyncToken& sync_token);
-  void Destroy();
 
- protected:
-  ~OwnedMailbox() override;
-
-  // ImageTransportFactoryObserver implementation.
-  void OnLostSharedContext() override;
-  void OnLostVizProcess() override;
+  // Returns a SingleReleaseCallback for TextureLayer.
+  std::unique_ptr<viz::SingleReleaseCallback> GetSingleReleaseCallback();
 
  private:
   friend class base::RefCounted<OwnedMailbox>;
 
+  ~OwnedMailbox();
+
+  void UpdateSyncToken(const gpu::SyncToken& sync_token, bool is_lost);
+
   gpu::gles2::GLES2Interface* const gl_;
   uint32_t texture_id_;
   gpu::MailboxHolder mailbox_holder_;
+  base::WeakPtrFactory<OwnedMailbox> weak_ptr_factory_;
 };
 
 }  // namespace content
diff --git a/content/browser/compositor/reflector_impl.cc b/content/browser/compositor/reflector_impl.cc
index a0a54c0..b41cb5b 100644
--- a/content/browser/compositor/reflector_impl.cc
+++ b/content/browser/compositor/reflector_impl.cc
@@ -4,7 +4,6 @@
 
 #include "content/browser/compositor/reflector_impl.h"
 
-#include "base/bind.h"
 #include "base/location.h"
 #include "components/viz/common/resources/transferable_resource.h"
 #include "content/browser/compositor/browser_compositor_output_surface.h"
@@ -30,8 +29,7 @@
     AddMirroringLayer(mirroring_layer);
 }
 
-ReflectorImpl::~ReflectorImpl() {
-}
+ReflectorImpl::~ReflectorImpl() = default;
 
 void ReflectorImpl::Shutdown() {
   if (output_surface_)
@@ -43,8 +41,8 @@
 void ReflectorImpl::DetachFromOutputSurface() {
   DCHECK(output_surface_);
   output_surface_->SetReflector(nullptr);
-  DCHECK(mailbox_.get());
-  mailbox_ = nullptr;
+  DCHECK(mailbox_);
+  mailbox_.reset();
   output_surface_ = nullptr;
   for (const auto& layer_data : mirroring_layers_)
     layer_data->layer->SetShowSolidColorContent();
@@ -140,12 +138,6 @@
     UpdateTexture(layer_data.get(), surface_size, mirroring_rect);
 }
 
-static void ReleaseMailbox(scoped_refptr<OwnedMailbox> mailbox,
-                           const gpu::SyncToken& sync_token,
-                           bool is_lost) {
-  mailbox->UpdateSyncToken(sync_token);
-}
-
 std::vector<std::unique_ptr<ReflectorImpl::LayerData>>::iterator
 ReflectorImpl::FindLayerData(ui::Layer* layer) {
   return std::find_if(mirroring_layers_.begin(), mirroring_layers_.end(),
@@ -162,9 +154,7 @@
         viz::TransferableResource::MakeGL(mailbox_->holder().mailbox, GL_LINEAR,
                                           mailbox_->holder().texture_target,
                                           mailbox_->holder().sync_token),
-        viz::SingleReleaseCallback::Create(
-            base::BindOnce(ReleaseMailbox, mailbox_)),
-        source_size);
+        mailbox_->GetSingleReleaseCallback(), source_size);
     layer_data->needs_set_mailbox = false;
   } else {
     layer_data->layer->SetTextureSize(source_size);
diff --git a/content/browser/devtools/DEPS b/content/browser/devtools/DEPS
index 28ece8ad..93bf7150 100644
--- a/content/browser/devtools/DEPS
+++ b/content/browser/devtools/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+third_party/brotli",  # For compressed protocol.json.
+  "+content/shell/browser/shell.h", # for access to web contents from devtools_protocol_test_support.cc
   # V8 version info
   "+v8/include/v8-version-string.h",
 ]
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 5567dd78..c66b3c9 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -11,12 +11,8 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
 #include "base/logging.h"
-#include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/sys_info.h"
 #include "base/values.h"
 #include "build/build_config.h"
@@ -24,13 +20,13 @@
 #include "components/download/public/common/download_file_impl.h"
 #include "components/download/public/common/download_task_runner.h"
 #include "content/browser/devtools/protocol/devtools_download_manager_delegate.h"
+#include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
 #include "content/browser/download/download_manager_impl.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/interstitial_page_delegate.h"
 #include "content/public/browser/javascript_dialog_manager.h"
@@ -46,7 +42,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/slow_download_http_response.h"
@@ -55,10 +50,6 @@
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/shell/browser/shell_content_browser_client.h"
 #include "content/shell/browser/shell_download_manager_delegate.h"
-#include "content/test/content_browser_test_utils_internal.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/cert_test_util.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -84,10 +75,6 @@
 
 namespace {
 
-const char kIdParam[] = "id";
-const char kMethodParam[] = "method";
-const char kParamsParam[] = "params";
-
 // If |params| contains an explanation with a non-empty certificate list,
 // returns true and points |certificate| to the certificate list of the first
 // explanation that contains a nonempty certificate list. Otherwise returns
@@ -178,326 +165,6 @@
 
 }  // namespace
 
-class DevToolsProtocolTest : public ContentBrowserTest,
-                             public DevToolsAgentHostClient,
-                             public WebContentsDelegate {
- public:
-  typedef base::Callback<bool(base::DictionaryValue*)> NotificationMatcher;
-
-  DevToolsProtocolTest()
-      : last_sent_id_(0),
-        waiting_for_command_result_id_(0),
-        in_dispatch_(false),
-        agent_host_can_close_(false) {}
-
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-  }
-
- protected:
-  // WebContentsDelegate methods:
-  bool DidAddMessageToConsole(WebContents* source,
-                              int32_t level,
-                              const base::string16& message,
-                              int32_t line_no,
-                              const base::string16& source_id) override {
-    console_messages_.push_back(base::UTF16ToUTF8(message));
-    return true;
-  }
-
-  blink::WebSecurityStyle GetSecurityStyle(
-      content::WebContents* web_contents,
-      content::SecurityStyleExplanations* security_style_explanations)
-      override {
-    security_style_explanations->secure_explanations.push_back(
-        SecurityStyleExplanation(
-            "an explanation title", "an explanation summary",
-            "an explanation description", cert_,
-            blink::WebMixedContentContextType::kNotMixedContent));
-    return blink::kWebSecurityStyleNeutral;
-  }
-
-  base::DictionaryValue* SendCommand(
-      const std::string& method,
-      std::unique_ptr<base::DictionaryValue> params) {
-    return SendCommand(method, std::move(params), true);
-  }
-
-  base::DictionaryValue* SendCommand(
-      const std::string& method,
-      std::unique_ptr<base::DictionaryValue> params,
-      bool wait) {
-    in_dispatch_ = true;
-    base::DictionaryValue command;
-    command.SetInteger(kIdParam, ++last_sent_id_);
-    command.SetString(kMethodParam, method);
-    if (params)
-      command.Set(kParamsParam, std::move(params));
-
-    std::string json_command;
-    base::JSONWriter::Write(command, &json_command);
-    agent_host_->DispatchProtocolMessage(this, json_command);
-    // Some messages are dispatched synchronously.
-    // Only run loop if we are not finished yet.
-    if (in_dispatch_ && wait) {
-      WaitForResponse();
-      in_dispatch_ = false;
-      return result_.get();
-    }
-    in_dispatch_ = false;
-    return result_.get();
-  }
-
-  void WaitForResponse() {
-    waiting_for_command_result_id_ = last_sent_id_;
-    base::RunLoop().Run();
-  }
-
-  bool HasValue(const std::string& path) {
-    base::Value* value = nullptr;
-    return result_->Get(path, &value);
-  }
-
-  bool HasListItem(const std::string& path_to_list,
-                   const std::string& name,
-                   const std::string& value) {
-    base::ListValue* list;
-    if (!result_->GetList(path_to_list, &list))
-      return false;
-
-    for (size_t i = 0; i != list->GetSize(); i++) {
-      base::DictionaryValue* item;
-      if (!list->GetDictionary(i, &item))
-        return false;
-      std::string id;
-      if (!item->GetString(name, &id))
-        return false;
-      if (id == value)
-        return true;
-    }
-    return false;
-  }
-
-  void Attach() {
-    agent_host_ = DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
-    agent_host_->AttachClient(this);
-    shell()->web_contents()->SetDelegate(this);
-  }
-
-  void AttachToBrowserTarget() {
-    // Tethering domain is not used in tests.
-    agent_host_ = DevToolsAgentHost::CreateForBrowser(
-        nullptr, DevToolsAgentHost::CreateServerSocketCallback());
-    agent_host_->AttachClient(this);
-    shell()->web_contents()->SetDelegate(this);
-  }
-
-  void Detach() {
-    if (agent_host_) {
-      agent_host_->DetachClient(this);
-      agent_host_ = nullptr;
-    }
-  }
-
-  void TearDownOnMainThread() override { Detach(); }
-
-  std::unique_ptr<base::DictionaryValue> WaitForNotification(
-      const std::string& notification) {
-    return WaitForNotification(notification, false);
-  }
-
-  std::unique_ptr<base::DictionaryValue> WaitForNotification(
-      const std::string& notification,
-      bool allow_existing) {
-    if (allow_existing) {
-      for (size_t i = 0; i < notifications_.size(); i++) {
-        if (notifications_[i] == notification) {
-          std::unique_ptr<base::DictionaryValue> result =
-              std::move(notification_params_[i]);
-          notifications_.erase(notifications_.begin() + i);
-          notification_params_.erase(notification_params_.begin() + i);
-          return result;
-        }
-      }
-    }
-
-    waiting_for_notification_ = notification;
-    RunMessageLoop();
-    return std::move(waiting_for_notification_params_);
-  }
-
-  // Waits for a notification whose params, when passed to |matcher|, returns
-  // true. Existing notifications are allowed.
-  std::unique_ptr<base::DictionaryValue> WaitForMatchingNotification(
-      const std::string& notification,
-      const NotificationMatcher& matcher) {
-    for (size_t i = 0; i < notifications_.size(); i++) {
-      if (notifications_[i] == notification &&
-          matcher.Run(notification_params_[i].get())) {
-        std::unique_ptr<base::DictionaryValue> result =
-            std::move(notification_params_[i]);
-        notifications_.erase(notifications_.begin() + i);
-        notification_params_.erase(notification_params_.begin() + i);
-        return result;
-      }
-    }
-
-    waiting_for_notification_ = notification;
-    waiting_for_notification_matcher_ = matcher;
-    RunMessageLoop();
-    return std::move(waiting_for_notification_params_);
-  }
-
-  void ClearNotifications() {
-    notifications_.clear();
-    notification_params_.clear();
-  }
-
-  struct ExpectedNavigation {
-    std::string url;
-    bool is_redirect;
-    bool abort;
-  };
-
-  std::string RemovePort(const GURL& url) {
-    GURL::Replacements remove_port;
-    remove_port.ClearPort();
-    return url.ReplaceComponents(remove_port).spec();
-  }
-
-  // Waits for the expected navigations to occur in any order. If an expected
-  // navigation occurs, Network.continueInterceptedRequest is called with the
-  // specified navigation_response to either allow it to proceed or to cancel
-  // it.
-  void ProcessNavigationsAnyOrder(
-      std::vector<ExpectedNavigation> expected_navigations) {
-    std::unique_ptr<base::DictionaryValue> params;
-    while (!expected_navigations.empty()) {
-      std::unique_ptr<base::DictionaryValue> params =
-          WaitForNotification("Network.requestIntercepted");
-
-      std::string interception_id;
-      ASSERT_TRUE(params->GetString("interceptionId", &interception_id));
-      bool is_redirect = params->HasKey("redirectUrl");
-      bool is_navigation;
-      ASSERT_TRUE(params->GetBoolean("isNavigationRequest", &is_navigation));
-      std::string resource_type;
-      ASSERT_TRUE(params->GetString("resourceType", &resource_type));
-      std::string url;
-      ASSERT_TRUE(params->GetString("request.url", &url));
-      if (is_redirect)
-        ASSERT_TRUE(params->GetString("redirectUrl", &url));
-      // The url will typically have a random port which we want to remove.
-      url = RemovePort(GURL(url));
-
-      if (!is_navigation) {
-        params.reset(new base::DictionaryValue());
-        params->SetString("interceptionId", interception_id);
-        SendCommand("Network.continueInterceptedRequest", std::move(params),
-                    false);
-        continue;
-      }
-
-      bool navigation_was_expected = false;
-      for (auto it = expected_navigations.begin();
-           it != expected_navigations.end(); it++) {
-        if (url != it->url || is_redirect != it->is_redirect)
-          continue;
-
-        params.reset(new base::DictionaryValue());
-        params->SetString("interceptionId", interception_id);
-        if (it->abort)
-          params->SetString("errorReason", "Aborted");
-        SendCommand("Network.continueInterceptedRequest", std::move(params),
-                    false);
-
-        navigation_was_expected = true;
-        expected_navigations.erase(it);
-        break;
-      }
-      EXPECT_TRUE(navigation_was_expected)
-          << "url = " << url << "is_redirect = " << is_redirect;
-    }
-  }
-
-  std::vector<std::string> GetAllFrameUrls() {
-    std::vector<std::string> urls;
-    for (RenderFrameHost* render_frame_host :
-         shell()->web_contents()->GetAllFrames()) {
-      urls.push_back(RemovePort(render_frame_host->GetLastCommittedURL()));
-    }
-    return urls;
-  }
-
-  void set_agent_host_can_close() { agent_host_can_close_ = true; }
-
-  void SetSecurityExplanationCert(
-      const scoped_refptr<net::X509Certificate>& cert) {
-    cert_ = cert;
-  }
-
-  std::unique_ptr<base::DictionaryValue> result_;
-  scoped_refptr<DevToolsAgentHost> agent_host_;
-  int last_sent_id_;
-  std::vector<int> result_ids_;
-  std::vector<std::string> notifications_;
-  std::vector<std::string> console_messages_;
-  std::vector<std::unique_ptr<base::DictionaryValue>> notification_params_;
-
- private:
-  void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
-                               const std::string& message) override {
-    std::unique_ptr<base::DictionaryValue> root(
-        static_cast<base::DictionaryValue*>(
-            base::JSONReader::Read(message).release()));
-    int id;
-    if (root->GetInteger("id", &id)) {
-      result_ids_.push_back(id);
-      base::DictionaryValue* result;
-      ASSERT_TRUE(root->GetDictionary("result", &result));
-      result_.reset(result->DeepCopy());
-      in_dispatch_ = false;
-      if (id && id == waiting_for_command_result_id_) {
-        waiting_for_command_result_id_ = 0;
-        base::RunLoop::QuitCurrentDeprecated();
-      }
-    } else {
-      std::string notification;
-      EXPECT_TRUE(root->GetString("method", &notification));
-      notifications_.push_back(notification);
-      base::DictionaryValue* params;
-      if (root->GetDictionary("params", &params)) {
-        notification_params_.push_back(params->CreateDeepCopy());
-      } else {
-        notification_params_.push_back(
-            base::WrapUnique(new base::DictionaryValue()));
-      }
-      if (waiting_for_notification_ == notification &&
-          (waiting_for_notification_matcher_.is_null() ||
-           waiting_for_notification_matcher_.Run(
-               notification_params_[notification_params_.size() - 1].get()))) {
-        waiting_for_notification_ = std::string();
-        waiting_for_notification_matcher_ = NotificationMatcher();
-        waiting_for_notification_params_ = base::WrapUnique(
-            notification_params_[notification_params_.size() - 1]->DeepCopy());
-        base::RunLoop::QuitCurrentDeprecated();
-      }
-    }
-  }
-
-  void AgentHostClosed(DevToolsAgentHost* agent_host) override {
-    if (!agent_host_can_close_)
-      NOTREACHED();
-  }
-
-  std::string waiting_for_notification_;
-  NotificationMatcher waiting_for_notification_matcher_;
-  std::unique_ptr<base::DictionaryValue> waiting_for_notification_params_;
-  int waiting_for_command_result_id_;
-  bool in_dispatch_;
-  bool agent_host_can_close_;
-  scoped_refptr<net::X509Certificate> cert_;
-};
 
 class TestInterstitialDelegate : public InterstitialPageDelegate {
  private:
diff --git a/content/browser/devtools/protocol/devtools_protocol_test_support.cc b/content/browser/devtools/protocol/devtools_protocol_test_support.cc
new file mode 100644
index 0000000..41ba4bc
--- /dev/null
+++ b/content/browser/devtools/protocol/devtools_protocol_test_support.cc
@@ -0,0 +1,285 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/security_style_explanations.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace content {
+
+namespace {
+
+const char kIdParam[] = "id";
+const char kMethodParam[] = "method";
+const char kParamsParam[] = "params";
+
+}  // namespace
+
+DevToolsProtocolTest::DevToolsProtocolTest()
+    : last_sent_id_(0),
+      waiting_for_command_result_id_(0),
+      in_dispatch_(false),
+      agent_host_can_close_(false) {}
+
+DevToolsProtocolTest::~DevToolsProtocolTest() = default;
+
+void DevToolsProtocolTest::SetUpOnMainThread() {
+  host_resolver()->AddRule("*", "127.0.0.1");
+}
+
+bool DevToolsProtocolTest::DidAddMessageToConsole(
+    WebContents* source,
+    int32_t level,
+    const base::string16& message,
+    int32_t line_no,
+    const base::string16& source_id) {
+  console_messages_.push_back(base::UTF16ToUTF8(message));
+  return true;
+}
+
+base::DictionaryValue* DevToolsProtocolTest::SendCommand(
+    const std::string& method,
+    std::unique_ptr<base::DictionaryValue> params,
+    bool wait) {
+  in_dispatch_ = true;
+  base::DictionaryValue command;
+  command.SetInteger(kIdParam, ++last_sent_id_);
+  command.SetString(kMethodParam, method);
+  if (params)
+    command.Set(kParamsParam, std::move(params));
+
+  std::string json_command;
+  base::JSONWriter::Write(command, &json_command);
+  agent_host_->DispatchProtocolMessage(this, json_command);
+  // Some messages are dispatched synchronously.
+  // Only run loop if we are not finished yet.
+  if (in_dispatch_ && wait) {
+    WaitForResponse();
+    in_dispatch_ = false;
+    return result_.get();
+  }
+  in_dispatch_ = false;
+  return result_.get();
+}
+
+void DevToolsProtocolTest::WaitForResponse() {
+  waiting_for_command_result_id_ = last_sent_id_;
+  RunLoopUpdatingQuitClosure();
+}
+
+bool DevToolsProtocolTest::HasValue(const std::string& path) {
+  base::Value* value = nullptr;
+  return result_->Get(path, &value);
+}
+
+bool DevToolsProtocolTest::HasListItem(const std::string& path_to_list,
+                                       const std::string& name,
+                                       const std::string& value) {
+  base::ListValue* list;
+  if (!result_->GetList(path_to_list, &list))
+    return false;
+
+  for (size_t i = 0; i != list->GetSize(); i++) {
+    base::DictionaryValue* item;
+    if (!list->GetDictionary(i, &item))
+      return false;
+    std::string id;
+    if (!item->GetString(name, &id))
+      return false;
+    if (id == value)
+      return true;
+  }
+  return false;
+}
+
+void DevToolsProtocolTest::Attach() {
+  agent_host_ = DevToolsAgentHost::GetOrCreateFor(shell()->web_contents());
+  agent_host_->AttachClient(this);
+  shell()->web_contents()->SetDelegate(this);
+}
+
+void DevToolsProtocolTest::AttachToBrowserTarget() {
+  // Tethering domain is not used in tests.
+  agent_host_ = DevToolsAgentHost::CreateForBrowser(
+      nullptr, DevToolsAgentHost::CreateServerSocketCallback());
+  agent_host_->AttachClient(this);
+  shell()->web_contents()->SetDelegate(this);
+}
+
+void DevToolsProtocolTest::TearDownOnMainThread() {
+  Detach();
+}
+
+std::unique_ptr<base::DictionaryValue>
+DevToolsProtocolTest::WaitForNotification(const std::string& notification,
+                                          bool allow_existing) {
+  if (allow_existing) {
+    for (size_t i = 0; i < notifications_.size(); i++) {
+      if (notifications_[i] == notification) {
+        std::unique_ptr<base::DictionaryValue> result =
+            std::move(notification_params_[i]);
+        notifications_.erase(notifications_.begin() + i);
+        notification_params_.erase(notification_params_.begin() + i);
+        return result;
+      }
+    }
+  }
+
+  waiting_for_notification_ = notification;
+  RunLoopUpdatingQuitClosure();
+  return std::move(waiting_for_notification_params_);
+}
+
+blink::WebSecurityStyle DevToolsProtocolTest::GetSecurityStyle(
+    content::WebContents* web_contents,
+    content::SecurityStyleExplanations* security_style_explanations) {
+  security_style_explanations->secure_explanations.push_back(
+      SecurityStyleExplanation(
+          "an explanation title", "an explanation summary",
+          "an explanation description", cert_,
+          blink::WebMixedContentContextType::kNotMixedContent));
+  return blink::kWebSecurityStyleNeutral;
+}
+
+std::unique_ptr<base::DictionaryValue>
+DevToolsProtocolTest::WaitForMatchingNotification(
+    const std::string& notification,
+    const NotificationMatcher& matcher) {
+  for (size_t i = 0; i < notifications_.size(); i++) {
+    if (notifications_[i] == notification &&
+        matcher.Run(notification_params_[i].get())) {
+      std::unique_ptr<base::DictionaryValue> result =
+          std::move(notification_params_[i]);
+      notifications_.erase(notifications_.begin() + i);
+      notification_params_.erase(notification_params_.begin() + i);
+      return result;
+    }
+  }
+
+  waiting_for_notification_ = notification;
+  waiting_for_notification_matcher_ = matcher;
+  RunLoopUpdatingQuitClosure();
+  return std::move(waiting_for_notification_params_);
+}
+
+void DevToolsProtocolTest::ProcessNavigationsAnyOrder(
+    std::vector<ExpectedNavigation> expected_navigations) {
+  std::unique_ptr<base::DictionaryValue> params;
+  while (!expected_navigations.empty()) {
+    std::unique_ptr<base::DictionaryValue> params =
+        WaitForNotification("Network.requestIntercepted");
+
+    std::string interception_id;
+    ASSERT_TRUE(params->GetString("interceptionId", &interception_id));
+    bool is_redirect = params->HasKey("redirectUrl");
+    bool is_navigation;
+    ASSERT_TRUE(params->GetBoolean("isNavigationRequest", &is_navigation));
+    std::string resource_type;
+    ASSERT_TRUE(params->GetString("resourceType", &resource_type));
+    std::string url;
+    ASSERT_TRUE(params->GetString("request.url", &url));
+    if (is_redirect)
+      ASSERT_TRUE(params->GetString("redirectUrl", &url));
+    // The url will typically have a random port which we want to remove.
+    url = RemovePort(GURL(url));
+
+    if (!is_navigation) {
+      params.reset(new base::DictionaryValue());
+      params->SetString("interceptionId", interception_id);
+      SendCommand("Network.continueInterceptedRequest", std::move(params),
+                  false);
+      continue;
+    }
+
+    bool navigation_was_expected = false;
+    for (auto it = expected_navigations.begin();
+         it != expected_navigations.end(); it++) {
+      if (url != it->url || is_redirect != it->is_redirect)
+        continue;
+
+      params.reset(new base::DictionaryValue());
+      params->SetString("interceptionId", interception_id);
+      if (it->abort)
+        params->SetString("errorReason", "Aborted");
+      SendCommand("Network.continueInterceptedRequest", std::move(params),
+                  false);
+
+      navigation_was_expected = true;
+      expected_navigations.erase(it);
+      break;
+    }
+    EXPECT_TRUE(navigation_was_expected)
+        << "url = " << url << "is_redirect = " << is_redirect;
+  }
+}
+
+void DevToolsProtocolTest::RunLoopUpdatingQuitClosure() {
+  base::RunLoop run_loop;
+  run_loop_quit_closure_ = run_loop.QuitClosure();
+  run_loop.Run();
+}
+
+void DevToolsProtocolTest::DispatchProtocolMessage(
+    DevToolsAgentHost* agent_host,
+    const std::string& message) {
+  std::unique_ptr<base::DictionaryValue> root(
+      static_cast<base::DictionaryValue*>(
+          base::JSONReader::Read(message).release()));
+  int id;
+  if (root->GetInteger("id", &id)) {
+    result_ids_.push_back(id);
+    base::DictionaryValue* result;
+    ASSERT_TRUE(root->GetDictionary("result", &result));
+    result_.reset(result->DeepCopy());
+    in_dispatch_ = false;
+    if (id && id == waiting_for_command_result_id_) {
+      waiting_for_command_result_id_ = 0;
+      std::move(run_loop_quit_closure_).Run();
+    }
+  } else {
+    std::string notification;
+    EXPECT_TRUE(root->GetString("method", &notification));
+    notifications_.push_back(notification);
+    base::DictionaryValue* params;
+    if (root->GetDictionary("params", &params)) {
+      notification_params_.push_back(params->CreateDeepCopy());
+    } else {
+      notification_params_.push_back(
+          base::WrapUnique(new base::DictionaryValue()));
+    }
+    if (waiting_for_notification_ == notification &&
+        (waiting_for_notification_matcher_.is_null() ||
+         waiting_for_notification_matcher_.Run(
+             notification_params_[notification_params_.size() - 1].get()))) {
+      waiting_for_notification_ = std::string();
+      waiting_for_notification_matcher_ = NotificationMatcher();
+      waiting_for_notification_params_ = base::WrapUnique(
+          notification_params_[notification_params_.size() - 1]->DeepCopy());
+      std::move(run_loop_quit_closure_).Run();
+    }
+  }
+}
+
+std::vector<std::string> DevToolsProtocolTest::GetAllFrameUrls() {
+  std::vector<std::string> urls;
+  for (RenderFrameHost* render_frame_host :
+       shell()->web_contents()->GetAllFrames()) {
+    urls.push_back(RemovePort(render_frame_host->GetLastCommittedURL()));
+  }
+  return urls;
+}
+
+void DevToolsProtocolTest::AgentHostClosed(DevToolsAgentHost* agent_host) {
+  if (!agent_host_can_close_)
+    NOTREACHED();
+}
+
+}  // namespace content
diff --git a/content/browser/devtools/protocol/devtools_protocol_test_support.h b/content/browser/devtools/protocol/devtools_protocol_test_support.h
new file mode 100644
index 0000000..15e5d23a
--- /dev/null
+++ b/content/browser/devtools/protocol/devtools_protocol_test_support.h
@@ -0,0 +1,150 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_PROTOCOL_TEST_SUPPORT_H_
+#define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_PROTOCOL_TEST_SUPPORT_H_
+
+#include <memory>
+#include <string>
+#include "base/callback.h"
+#include "base/values.h"
+#include "content/public/browser/devtools_agent_host.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/test/content_browser_test.h"
+#include "net/test/cert_test_util.h"
+
+namespace content {
+
+class DevToolsProtocolTest : public ContentBrowserTest,
+                             public DevToolsAgentHostClient,
+                             public WebContentsDelegate {
+ public:
+  typedef base::RepeatingCallback<bool(base::DictionaryValue*)>
+      NotificationMatcher;
+
+  DevToolsProtocolTest();
+  ~DevToolsProtocolTest() override;
+
+  void SetUpOnMainThread() override;
+
+ protected:
+  // WebContentsDelegate methods:
+  bool DidAddMessageToConsole(WebContents* source,
+                              int32_t level,
+                              const base::string16& message,
+                              int32_t line_no,
+                              const base::string16& source_id) override;
+
+  blink::WebSecurityStyle GetSecurityStyle(
+      content::WebContents* web_contents,
+      content::SecurityStyleExplanations* security_style_explanations) override;
+
+  base::DictionaryValue* SendCommand(
+      const std::string& method,
+      std::unique_ptr<base::DictionaryValue> params) {
+    return SendCommand(method, std::move(params), true);
+  }
+
+  base::DictionaryValue* SendCommand(
+      const std::string& method,
+      std::unique_ptr<base::DictionaryValue> params,
+      bool wait);
+
+  void WaitForResponse();
+
+  bool HasValue(const std::string& path);
+
+  bool HasListItem(const std::string& path_to_list,
+                   const std::string& name,
+                   const std::string& value);
+
+  void Attach();
+
+  void AttachToBrowserTarget();
+
+  void Detach() {
+    if (agent_host_) {
+      agent_host_->DetachClient(this);
+      agent_host_ = nullptr;
+    }
+  }
+
+  void TearDownOnMainThread() override;
+
+  std::unique_ptr<base::DictionaryValue> WaitForNotification(
+      const std::string& notification) {
+    return WaitForNotification(notification, false);
+  }
+
+  std::unique_ptr<base::DictionaryValue> WaitForNotification(
+      const std::string& notification,
+      bool allow_existing);
+
+  // Waits for a notification whose params, when passed to |matcher|, returns
+  // true. Existing notifications are allowed.
+  std::unique_ptr<base::DictionaryValue> WaitForMatchingNotification(
+      const std::string& notification,
+      const NotificationMatcher& matcher);
+
+  void ClearNotifications() {
+    notifications_.clear();
+    notification_params_.clear();
+  }
+
+  struct ExpectedNavigation {
+    std::string url;
+    bool is_redirect;
+    bool abort;
+  };
+
+  std::string RemovePort(const GURL& url) {
+    GURL::Replacements remove_port;
+    remove_port.ClearPort();
+    return url.ReplaceComponents(remove_port).spec();
+  }
+
+  // Waits for the expected navigations to occur in any order. If an expected
+  // navigation occurs, Network.continueInterceptedRequest is called with the
+  // specified navigation_response to either allow it to proceed or to cancel
+  // it.
+  void ProcessNavigationsAnyOrder(
+      std::vector<ExpectedNavigation> expected_navigations);
+
+  std::vector<std::string> GetAllFrameUrls();
+
+  void set_agent_host_can_close() { agent_host_can_close_ = true; }
+
+  void SetSecurityExplanationCert(
+      const scoped_refptr<net::X509Certificate>& cert) {
+    cert_ = cert;
+  }
+
+  std::unique_ptr<base::DictionaryValue> result_;
+  scoped_refptr<DevToolsAgentHost> agent_host_;
+  int last_sent_id_;
+  std::vector<int> result_ids_;
+  std::vector<std::string> notifications_;
+  std::vector<std::string> console_messages_;
+  std::vector<std::unique_ptr<base::DictionaryValue>> notification_params_;
+
+ private:
+  void RunLoopUpdatingQuitClosure();
+  void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
+                               const std::string& message) override;
+
+  void AgentHostClosed(DevToolsAgentHost* agent_host) override;
+
+  std::string waiting_for_notification_;
+  NotificationMatcher waiting_for_notification_matcher_;
+  std::unique_ptr<base::DictionaryValue> waiting_for_notification_params_;
+  int waiting_for_command_result_id_;
+  bool in_dispatch_;
+  bool agent_host_can_close_;
+  scoped_refptr<net::X509Certificate> cert_;
+  base::OnceClosure run_loop_quit_closure_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_DEVTOOLS_PROTOCOL_TEST_SUPPORT_H_
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 7fe66cd..4fbe6ed 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -404,11 +404,6 @@
                                                    options.fromMaybe(""));
   }
 
-  // If inspected target is a render process Tracing.start will be handled by
-  // tracing agent in the renderer.
-  if (frame_tree_node_)
-    callback->fallThrough();
-
   SetupProcessFilter(nullptr);
 
   TracingController::GetInstance()->StartTracing(
@@ -462,14 +457,12 @@
       trace_config_, base::RepeatingCallback<void()>());
 }
 
-void TracingHandler::End(std::unique_ptr<EndCallback> callback) {
+Response TracingHandler::End() {
   // Startup tracing triggered by --trace-config-file is a special case, where
   // tracing is started automatically upon browser startup and can be stopped
   // via DevTools.
-  if (!did_initiate_recording_ && !IsStartupTracingActive()) {
-    callback->sendFailure(Response::Error("Tracing is not started"));
-    return;
-  }
+  if (!did_initiate_recording_ && !IsStartupTracingActive())
+    return Response::Error("Tracing is not started");
 
   scoped_refptr<TracingController::TraceDataEndpoint> endpoint;
   if (return_as_stream_) {
@@ -488,12 +481,8 @@
     endpoint = new DevToolsTraceEndpointProxy(weak_factory_.GetWeakPtr());
     StopTracing(endpoint, tracing::mojom::kChromeTraceEventLabel);
   }
-  // If inspected target is a render process Tracing.end will be handled by
-  // tracing agent in the renderer.
-  if (frame_tree_node_)
-    callback->fallThrough();
-  else
-    callback->sendSuccess();
+
+  return Response::OK();
 }
 
 void TracingHandler::GetCategories(
@@ -508,8 +497,7 @@
     std::unique_ptr<StartCallback> callback) {
   EmitFrameTree();
 
-  if (!frame_tree_node_)
-    callback->sendSuccess();
+  callback->sendSuccess();
 
   bool screenshot_enabled;
   TRACE_EVENT_CATEGORY_GROUP_ENABLED(
diff --git a/content/browser/devtools/protocol/tracing_handler.h b/content/browser/devtools/protocol/tracing_handler.h
index 82f11ef..fc3336a 100644
--- a/content/browser/devtools/protocol/tracing_handler.h
+++ b/content/browser/devtools/protocol/tracing_handler.h
@@ -69,7 +69,7 @@
              Maybe<std::string> transfer_compression,
              Maybe<Tracing::TraceConfig> config,
              std::unique_ptr<StartCallback> callback) override;
-  void End(std::unique_ptr<EndCallback> callback) override;
+  Response End() override;
   void GetCategories(std::unique_ptr<GetCategoriesCallback> callback) override;
   void RequestMemoryDump(
       std::unique_ptr<RequestMemoryDumpCallback> callback) override;
diff --git a/content/browser/devtools/protocol_config.json b/content/browser/devtools/protocol_config.json
index 406ef051..3746763 100644
--- a/content/browser/devtools/protocol_config.json
+++ b/content/browser/devtools/protocol_config.json
@@ -85,7 +85,7 @@
             },
             {
                 "domain": "Tracing",
-                "async": ["start", "end", "getCategories", "requestMemoryDump"]
+                "async": ["start", "getCategories", "requestMemoryDump"]
             }
         ]
     },
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.cc b/content/browser/notifications/notification_event_dispatcher_impl.cc
index 56c433dd..0226226 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl.cc
+++ b/content/browser/notifications/notification_event_dispatcher_impl.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/common/persistent_notification_status.h"
 #include "content/public/common/platform_notification_data.h"
 
 namespace content {
@@ -57,13 +58,13 @@
             << blink::ServiceWorkerStatusToString(service_worker_status);
 #endif
 
-  PersistentNotificationStatus status = PERSISTENT_NOTIFICATION_STATUS_SUCCESS;
+  PersistentNotificationStatus status = PersistentNotificationStatus::kSuccess;
   switch (service_worker_status) {
     case blink::ServiceWorkerStatusCode::kOk:
       // Success status was initialized above.
       break;
     case blink::ServiceWorkerStatusCode::kErrorEventWaitUntilRejected:
-      status = PERSISTENT_NOTIFICATION_STATUS_EVENT_WAITUNTIL_REJECTED;
+      status = PersistentNotificationStatus::kWaitUntilRejected;
       break;
     case blink::ServiceWorkerStatusCode::kErrorFailed:
     case blink::ServiceWorkerStatusCode::kErrorAbort:
@@ -82,7 +83,7 @@
     case blink::ServiceWorkerStatusCode::kErrorDiskCache:
     case blink::ServiceWorkerStatusCode::kErrorRedundant:
     case blink::ServiceWorkerStatusCode::kErrorDisallowed:
-      status = PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR;
+      status = PersistentNotificationStatus::kServiceWorkerError;
       break;
   }
   NotificationEventFinished(dispatch_complete_callback, status);
@@ -114,10 +115,10 @@
     return;
   }
 
-  PersistentNotificationStatus status = PERSISTENT_NOTIFICATION_STATUS_SUCCESS;
+  PersistentNotificationStatus status = PersistentNotificationStatus::kSuccess;
   switch (service_worker_status) {
     case blink::ServiceWorkerStatusCode::kErrorNotFound:
-      status = PERSISTENT_NOTIFICATION_STATUS_NO_SERVICE_WORKER;
+      status = PersistentNotificationStatus::kServiceWorkerMissing;
       break;
     case blink::ServiceWorkerStatusCode::kErrorFailed:
     case blink::ServiceWorkerStatusCode::kErrorAbort:
@@ -136,7 +137,7 @@
     case blink::ServiceWorkerStatusCode::kErrorDiskCache:
     case blink::ServiceWorkerStatusCode::kErrorRedundant:
     case blink::ServiceWorkerStatusCode::kErrorDisallowed:
-      status = PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR;
+      status = PersistentNotificationStatus::kServiceWorkerError;
       break;
     case blink::ServiceWorkerStatusCode::kOk:
       NOTREACHED();
@@ -169,7 +170,7 @@
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::BindOnce(dispatch_error_callback,
-                       PERSISTENT_NOTIFICATION_STATUS_DATABASE_ERROR));
+                       PersistentNotificationStatus::kDatabaseError));
     return;
   }
 
@@ -259,10 +260,10 @@
                                            service_worker_status);
     return;
   }
-  NotificationEventFinished(
-      dispatch_complete_callback,
-      success ? PERSISTENT_NOTIFICATION_STATUS_SUCCESS
-              : PERSISTENT_NOTIFICATION_STATUS_DATABASE_ERROR);
+  NotificationEventFinished(dispatch_complete_callback,
+                            success
+                                ? PersistentNotificationStatus::kSuccess
+                                : PersistentNotificationStatus::kDatabaseError);
 }
 
 // Called when the persistent notification close event has been handled
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 926f3631..cc8c89b 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -63,8 +63,7 @@
   switch (gesture_event->GetType()) {
     case WebInputEvent::kGestureScrollBegin: {
       DCHECK(!suppress_manipulation_events_);
-      DCHECK(!touchscreen_scroll_in_progress_);
-      touchscreen_scroll_in_progress_ = true;
+      gesture_sequence_in_progress_ = true;
       gesture_sequence_.append("B");
       // TODO(https://crbug.com/851644): Make sure the value is properly set.
       if (!scrolling_touch_action_.has_value()) {
@@ -115,8 +114,7 @@
 
     case WebInputEvent::kGestureScrollEnd:
       gesture_sequence_.clear();
-      DCHECK(touchscreen_scroll_in_progress_);
-      touchscreen_scroll_in_progress_ = false;
+      gesture_sequence_in_progress_ = false;
       ReportGestureEventFiltered(suppress_manipulation_events_);
       return FilterManipulationEventAndResetState()
                  ? FilterGestureEventResult::kFilterGestureEventFiltered
@@ -134,6 +132,7 @@
     // The double tap gesture is a tap ending event. If a double tap gesture is
     // filtered out, replace it with a tap event.
     case WebInputEvent::kGestureDoubleTap:
+      gesture_sequence_in_progress_ = false;
       gesture_sequence_.append("D");
       DCHECK_EQ(1, gesture_event->data.tap.tap_count);
       if (!allow_current_double_tap_event_)
@@ -167,6 +166,7 @@
 
     case WebInputEvent::kGestureTap:
     case WebInputEvent::kGestureTapCancel:
+      gesture_sequence_in_progress_ = false;
       gesture_sequence_.append("A");
       if (drop_current_tap_ending_event_) {
         drop_current_tap_ending_event_ = false;
@@ -176,6 +176,7 @@
 
     case WebInputEvent::kGestureTapDown:
       gesture_sequence_.append("O");
+      gesture_sequence_in_progress_ = true;
       // If the gesture is hitting a region that has a non-blocking (such as a
       // passive) event listener.
       if (gesture_event->is_source_touch_event_set_non_blocking)
@@ -362,8 +363,9 @@
   // If a page has a touch event handler, this function can be called twice with
   // has_handlers = false first and then true later. When it is true, we need to
   // reset the |scrolling_touch_action_|. However, we do not want to reset it if
-  // there is an active scroll in progress.
-  if (has_touch_event_handler_ && !touchscreen_scroll_in_progress_)
+  // there is an active gesture sequence in progress. For example, the
+  // OnHasTouchEventHandlers(true) can be received after a GestureTapDown.
+  if (has_touch_event_handler_ && !gesture_sequence_in_progress_)
     scrolling_touch_action_.reset();
 }
 
diff --git a/content/browser/renderer_host/input/touch_action_filter.h b/content/browser/renderer_host/input/touch_action_filter.h
index c43f164..328848675 100644
--- a/content/browser/renderer_host/input/touch_action_filter.h
+++ b/content/browser/renderer_host/input/touch_action_filter.h
@@ -105,9 +105,9 @@
   // TODO(https://crbug.com/850238): default to true or make it Optional.
   bool has_touch_event_handler_ = false;
 
-  // True if an active touch scroll gesture is in progress. i.e. after GSB and
+  // True if an active gesture sequence is in progress. i.e. after GTD and
   // before GSE.
-  bool touchscreen_scroll_in_progress_ = false;
+  bool gesture_sequence_in_progress_ = false;
 
   // What touch actions are currently permitted.
   base::Optional<cc::TouchAction> allowed_touch_action_;
diff --git a/content/browser/renderer_host/input/touch_action_filter_unittest.cc b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
index 9a5d4a8..740458e 100644
--- a/content/browser/renderer_host/input/touch_action_filter_unittest.cc
+++ b/content/browser/renderer_host/input/touch_action_filter_unittest.cc
@@ -892,6 +892,53 @@
   EXPECT_TRUE(ScrollingTouchAction().has_value());
 }
 
+// This test ensures that when the IPC message OnHasTouchEventHandlers is
+// received in the middle of a gesture sequence, the touch action is not reset.
+TEST_F(TouchActionFilterTest, OnHasTouchEventHandlersReceivedDuringScroll) {
+  filter_.OnHasTouchEventHandlers(false);
+
+  WebGestureEvent tap_down = SyntheticWebGestureEventBuilder::Build(
+      WebInputEvent::kGestureTapDown, kSourceDevice);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  filter_.OnHasTouchEventHandlers(true);
+  EXPECT_TRUE(ScrollingTouchAction().has_value());
+
+  filter_.OnSetTouchAction(cc::kTouchActionPan);
+  // Simulate a simple tap gesture.
+  WebGestureEvent tap = SyntheticWebGestureEventBuilder::Build(
+      WebInputEvent::kGestureTap, kSourceDevice);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  // Gesture tap indicates that there is no scroll in progress, so this should
+  // reset the |allowed_touch_action_|.
+  filter_.ResetTouchAction();
+  EXPECT_FALSE(filter_.allowed_touch_action().has_value());
+
+  filter_.OnHasTouchEventHandlers(false);
+  // Simulate a double tap gesture: GTD-->GT-->GTD-->GDT.
+  filter_.OnSetTouchAction(cc::kTouchActionPan);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  EXPECT_EQ(ScrollingTouchAction().value(), cc::kTouchActionPan);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  filter_.OnHasTouchEventHandlers(true);
+  EXPECT_FALSE(ScrollingTouchAction().has_value());
+  filter_.OnSetTouchAction(cc::kTouchActionPan);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  EXPECT_EQ(ScrollingTouchAction().value(), cc::kTouchActionPan);
+  WebGestureEvent double_tap = SyntheticWebGestureEventBuilder::Build(
+      WebInputEvent::kGestureDoubleTap, kSourceDevice);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap_down),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  EXPECT_EQ(filter_.FilterGestureEvent(&tap),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+  EXPECT_EQ(filter_.FilterGestureEvent(&double_tap),
+            FilterGestureEventResult::kFilterGestureEventAllowed);
+}
+
 TEST_F(TouchActionFilterTest, TouchpadScroll) {
   WebGestureEvent scroll_begin =
       SyntheticWebGestureEventBuilder::BuildScrollBegin(
diff --git a/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
index 7a83dc0..2aa6631 100644
--- a/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
+++ b/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
@@ -13,15 +13,20 @@
 #include "build/build_config.h"
 #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
 #include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/common/process_type.h"
 #include "content/public/common/socket_permission_request.h"
 #include "ipc/ipc_message_macros.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/log/net_log_source.h"
-#include "net/socket/udp_socket.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/private/ppb_net_address_private.h"
 #include "ppapi/host/dispatch_host_message.h"
@@ -33,6 +38,7 @@
 #include "ppapi/proxy/udp_socket_resource_constants.h"
 #include "ppapi/shared_impl/private/net_address_private_impl.h"
 #include "ppapi/shared_impl/socket_option_data.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/network/firewall_hole.h"
@@ -42,20 +48,23 @@
 using ppapi::host::NetErrorToPepperError;
 using ppapi::proxy::UDPSocketResourceConstants;
 
+namespace content {
+
 namespace {
 
+const PepperUDPSocketMessageFilter::CreateUDPSocketCallback*
+    g_create_udp_socket_callback_for_testing = nullptr;
+
 size_t g_num_udp_filter_instances = 0;
 
 }  // namespace
 
-namespace content {
-
 PepperUDPSocketMessageFilter::PendingSend::PendingSend(
     const net::IPAddress& address,
     int port,
-    const scoped_refptr<net::IOBufferWithSize>& buffer,
+    std::vector<uint8_t> data,
     const ppapi::host::ReplyMessageContext& context)
-    : address(address), port(port), buffer(buffer), context(context) {}
+    : address(address), port(port), data(std::move(data)), context(context) {}
 
 PepperUDPSocketMessageFilter::PendingSend::PendingSend(
     const PendingSend& other) = default;
@@ -80,7 +89,13 @@
       render_process_id_(0),
       render_frame_id_(0),
       is_potentially_secure_plugin_context_(
-          host->IsPotentiallySecurePluginContext(instance)) {
+          host->IsPotentiallySecurePluginContext(instance)),
+      binding_(this)
+#if defined(OS_CHROMEOS)
+      ,
+      firewall_hole_weak_ptr_factory_(this)
+#endif  //  defined(OS_CHROMEOS)
+{
   ++g_num_udp_filter_instances;
   DCHECK(host);
 
@@ -91,15 +106,35 @@
 }
 
 PepperUDPSocketMessageFilter::~PepperUDPSocketMessageFilter() {
-  Close();
+  DCHECK(closed_);
+  DCHECK(!socket_);
+  DCHECK(!binding_);
   --g_num_udp_filter_instances;
 }
 
+void PepperUDPSocketMessageFilter::SetCreateUDPSocketCallbackForTesting(
+    const CreateUDPSocketCallback* create_udp_socket_callback) {
+  DCHECK(!create_udp_socket_callback ||
+         !g_create_udp_socket_callback_for_testing);
+  g_create_udp_socket_callback_for_testing = create_udp_socket_callback;
+}
+
 // static
 size_t PepperUDPSocketMessageFilter::GetNumInstances() {
   return g_num_udp_filter_instances;
 }
 
+void PepperUDPSocketMessageFilter::OnFilterDestroyed() {
+  ResourceMessageFilter::OnFilterDestroyed();
+  // Need to close the socket on the UI thread. Calling Close() also ensures
+  // that future messages will be ignored, so the mojo pipes won't be
+  // re-created, so after Close() runs, |this| can be safely deleted on the IO
+  // thread.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(&PepperUDPSocketMessageFilter::Close, this));
+}
+
 scoped_refptr<base::TaskRunner>
 PepperUDPSocketMessageFilter::OverrideTaskRunnerForMessage(
     const IPC::Message& message) {
@@ -107,7 +142,6 @@
     case PpapiHostMsg_UDPSocket_SetOption::ID:
     case PpapiHostMsg_UDPSocket_Close::ID:
     case PpapiHostMsg_UDPSocket_RecvSlotAvailable::ID:
-      return BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
     case PpapiHostMsg_UDPSocket_Bind::ID:
     case PpapiHostMsg_UDPSocket_SendTo::ID:
     case PpapiHostMsg_UDPSocket_JoinGroup::ID:
@@ -142,14 +176,14 @@
     const ppapi::host::HostMessageContext* context,
     PP_UDPSocket_Option name,
     const ppapi::SocketOptionData& value) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (closed_)
     return PP_ERROR_FAILED;
 
   switch (name) {
     case PP_UDPSOCKET_OPTION_ADDRESS_REUSE: {
-      if (socket_.get()) {
+      if (socket_) {
         // AllowReuseAddress is only effective before Bind().
         // Note that this limitation originally comes from Windows, but
         // PPAPI tries to provide platform independent APIs.
@@ -172,16 +206,19 @@
       if (!value.GetBool(&boolean_value))
         return PP_ERROR_BADARGUMENT;
 
-      // If the socket is already bound, proxy the value to UDPSocket.
-      if (socket_.get())
-        return NetErrorToPepperError(socket_->SetBroadcast(boolean_value));
-
-      // UDPSocket instance is not yet created, so remember the value here.
       if (boolean_value) {
         socket_options_ |= SOCKET_OPTION_BROADCAST;
       } else {
         socket_options_ &= ~SOCKET_OPTION_BROADCAST;
       }
+
+      if (socket_) {
+        socket_->SetBroadcast(
+            boolean_value,
+            CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
+                context));
+        return PP_OK_COMPLETIONPENDING;
+      }
       return PP_OK;
     }
     case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE: {
@@ -190,15 +227,18 @@
           integer_value > UDPSocketResourceConstants::kMaxSendBufferSize)
         return PP_ERROR_BADARGUMENT;
 
-      // If the socket is already bound, proxy the value to UDPSocket.
-      if (socket_.get()) {
-        return NetErrorToPepperError(
-            socket_->SetSendBufferSize(integer_value));
-      }
-
-      // UDPSocket instance is not yet created, so remember the value here.
       socket_options_ |= SOCKET_OPTION_SNDBUF_SIZE;
       sndbuf_size_ = integer_value;
+
+      // If the socket is already initialized, proxy the value to UDPSocket.
+      if (socket_) {
+        socket_->SetSendBufferSize(
+            integer_value,
+            CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
+                context));
+        return PP_OK_COMPLETIONPENDING;
+      }
+
       return PP_OK;
     }
     case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
@@ -207,15 +247,18 @@
           integer_value > UDPSocketResourceConstants::kMaxReceiveBufferSize)
         return PP_ERROR_BADARGUMENT;
 
-      // If the socket is already bound, proxy the value to UDPSocket.
-      if (socket_.get()) {
-        return NetErrorToPepperError(
-            socket_->SetReceiveBufferSize(integer_value));
-      }
-
-      // UDPSocket instance is not yet created, so remember the value here.
       socket_options_ |= SOCKET_OPTION_RCVBUF_SIZE;
       rcvbuf_size_ = integer_value;
+
+      // If the socket is already initialized, proxy the value to UDPSocket.
+      if (socket_) {
+        socket_->SetReceiveBufferSize(
+            integer_value,
+            CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
+                context));
+        return PP_OK_COMPLETIONPENDING;
+      }
+
       return PP_OK;
     }
     case PP_UDPSOCKET_OPTION_MULTICAST_LOOP: {
@@ -223,21 +266,22 @@
       if (!value.GetBool(&boolean_value))
         return PP_ERROR_BADARGUMENT;
 
-      // If the socket is already bound, proxy the value to UDPSocket.
-      if (socket_) {
-        if (can_use_multicast_ != PP_OK)
-          return can_use_multicast_;
-
-        return NetErrorToPepperError(
-            socket_->SetMulticastLoopbackMode(boolean_value));
-      }
-
-      // UDPSocket instance is not yet created, so remember the value here.
       if (boolean_value) {
         socket_options_ |= SOCKET_OPTION_MULTICAST_LOOP;
       } else {
         socket_options_ &= ~SOCKET_OPTION_MULTICAST_LOOP;
       }
+
+      // If the socket is already initialized, either fail if permissions
+      // disallow multicast, or lie and claim it succeeded, to maintain previous
+      // behavior.
+      if (socket_) {
+        if (can_use_multicast_ != PP_OK)
+          return can_use_multicast_;
+
+        return PP_OK;
+      }
+
       return PP_OK;
     }
     case PP_UDPSOCKET_OPTION_MULTICAST_TTL: {
@@ -246,18 +290,20 @@
           integer_value < 0 || integer_value > 255)
         return PP_ERROR_BADARGUMENT;
 
-      // If the socket is already bound, proxy the value to UDPSocket.
+      // UDPSocket instance is not yet created, so remember the value here.
+      socket_options_ |= SOCKET_OPTION_MULTICAST_TTL;
+      multicast_ttl_ = integer_value;
+
+      // If the socket is already initialized, either fail if permissions
+      // disallow multicast, or lie and claim it succeeded, to maintain previous
+      // behavior.
       if (socket_) {
         if (can_use_multicast_ != PP_OK)
           return can_use_multicast_;
 
-        return NetErrorToPepperError(
-            socket_->SetMulticastTimeToLive(integer_value));
+        return PP_OK;
       }
 
-      // UDPSocket instance is not yet created, so remember the value here.
-      socket_options_ |= SOCKET_OPTION_MULTICAST_TTL;
-      multicast_ttl_ = integer_value;
       return PP_OK;
     }
     default: {
@@ -273,6 +319,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(context);
 
+  if (closed_ || socket_)
+    return PP_ERROR_FAILED;
+
   // Check for permissions to use multicast APIS. This check must be done while
   // on the UI thread, so we cache the value here to be used later on.
   PP_NetAddress_Private any_addr;
@@ -290,10 +339,81 @@
     return PP_ERROR_NOACCESS;
   }
 
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&PepperUDPSocketMessageFilter::DoBind, this,
-                     context->MakeReplyMessageContext(), addr));
+  net::IPAddressBytes address;
+  uint16_t port;
+  if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port))
+    return PP_ERROR_ADDRESS_INVALID;
+  net::IPEndPoint end_point(net::IPAddress(address), port);
+
+  network::mojom::UDPSocketOptionsPtr udp_socket_options =
+      network::mojom::UDPSocketOptions::New();
+  udp_socket_options->allow_address_reuse =
+      !!(socket_options_ & SOCKET_OPTION_ADDRESS_REUSE);
+  if (socket_options_ & SOCKET_OPTION_SNDBUF_SIZE)
+    udp_socket_options->send_buffer_size = sndbuf_size_;
+  if (socket_options_ & SOCKET_OPTION_RCVBUF_SIZE)
+    udp_socket_options->receive_buffer_size = rcvbuf_size_;
+
+  if (socket_options_ & SOCKET_OPTION_MULTICAST_LOOP) {
+    if (can_use_multicast_ != PP_OK) {
+      // TODO(mmenke):  The above line implies |can_use_multicast_| is a PP
+      // error code, but the next line implies it is a net error code. Fix that.
+      return NetErrorToPepperError(can_use_multicast_);
+    }
+    // TODO(mmenke): This doesn't seem to be doing anything - this is the
+    // default behavior.
+    udp_socket_options->multicast_loopback_mode = true;
+  }
+  if (socket_options_ & SOCKET_OPTION_MULTICAST_TTL) {
+    if (can_use_multicast_ != PP_OK) {
+      // TODO(mmenke):  The above line implies |can_use_multicast_| is a PP
+      // error code, but the next line implies it is a net error code. Fix that.
+      return NetErrorToPepperError(can_use_multicast_);
+    }
+
+    udp_socket_options->multicast_time_to_live = multicast_ttl_;
+  }
+
+  RenderFrameHost* render_frame_host =
+      RenderFrameHost::FromID(render_process_id_, render_frame_id_);
+  // If the RenderFrameHost has been closed, just fail the request.
+  if (!render_frame_host)
+    return PP_ERROR_NOACCESS;
+
+  network::mojom::UDPSocketReceiverPtr udp_socket_receiver;
+  // Avoid binding the receiver until the socket has been successfully Bound (in
+  // a socket sense), to avoid providing read data to the caller until it has
+  // been told that the socket was bound.
+  network::mojom::UDPSocketReceiverRequest receiver_request =
+      mojo::MakeRequest(&udp_socket_receiver);
+
+  SiteInstance* site_instance = render_frame_host->GetSiteInstance();
+  network::mojom::NetworkContext* network_context =
+      BrowserContext::GetStoragePartition(site_instance->GetBrowserContext(),
+                                          site_instance)
+          ->GetNetworkContext();
+  if (g_create_udp_socket_callback_for_testing) {
+    g_create_udp_socket_callback_for_testing->Run(
+        network_context, mojo::MakeRequest(&socket_),
+        std::move(udp_socket_receiver));
+  } else {
+    network_context->CreateUDPSocket(mojo::MakeRequest(&socket_),
+                                     std::move(udp_socket_receiver));
+  }
+
+  ppapi::host::ReplyMessageContext reply_context =
+      context->MakeReplyMessageContext();
+  // Watch the socket for errors during the the Bind call.
+  socket_.set_connection_error_handler(
+      base::BindOnce(&PepperUDPSocketMessageFilter::SendBindError,
+                     base::Unretained(this), reply_context, PP_ERROR_FAILED));
+
+  // This is the actual socket Bind call (i.e., not a Mojo Bind call).
+  socket_->Bind(end_point, std::move(udp_socket_options),
+                base::BindOnce(&PepperUDPSocketMessageFilter::DoBindCallback,
+                               base::Unretained(this),
+                               std::move(receiver_request), reply_context));
+
   return PP_OK_COMPLETIONPENDING;
 }
 
@@ -304,6 +424,11 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(context);
 
+  // Check |binding_| instead of |socket_| because |binding_| is only set
+  // after the Bind() call completes.
+  if (closed_ || !binding_)
+    return PP_ERROR_FAILED;
+
   SocketPermissionRequest request =
       pepper_socket_utils::CreateSocketPermissionRequest(
           SocketPermissionRequest::UDP_SEND_TO, addr);
@@ -315,32 +440,61 @@
     return PP_ERROR_NOACCESS;
   }
 
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::BindOnce(&PepperUDPSocketMessageFilter::DoSendTo, this,
-                     context->MakeReplyMessageContext(), data, addr));
+  // Make sure a malicious plugin can't queue up an unlimited number of buffers.
+  size_t num_pending_sends = pending_sends_.size();
+  if (num_pending_sends == UDPSocketResourceConstants::kPluginSendBufferSlots) {
+    return PP_ERROR_FAILED;
+  }
+
+  size_t num_bytes = data.size();
+  if (num_bytes == 0 ||
+      num_bytes >
+          static_cast<size_t>(UDPSocketResourceConstants::kMaxWriteSize)) {
+    // Size of |data| is checked on the plugin side.
+    NOTREACHED();
+    return PP_ERROR_BADARGUMENT;
+  }
+
+  net::IPAddressBytes address;
+  uint16_t port;
+  if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
+    return PP_ERROR_ADDRESS_INVALID;
+  }
+
+  const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data.data());
+  std::vector<uint8_t> data_vector(data_ptr, data_ptr + num_bytes);
+
+  pending_sends_.push(PendingSend(net::IPAddress(address), port,
+                                  std::move(data_vector),
+                                  context->MakeReplyMessageContext()));
+  // Can only start the send if there isn't another send pending.
+  if (num_pending_sends == 0)
+    StartPendingSend();
   return PP_OK_COMPLETIONPENDING;
 }
 
 int32_t PepperUDPSocketMessageFilter::OnMsgClose(
     const ppapi::host::HostMessageContext* context) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   Close();
   return PP_OK;
 }
 
 int32_t PepperUDPSocketMessageFilter::OnMsgRecvSlotAvailable(
     const ppapi::host::HostMessageContext* context) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (remaining_recv_slots_ <
       UDPSocketResourceConstants::kPluginReceiveBufferSlots) {
-    remaining_recv_slots_++;
-  }
+    // If the pipe was closed, but the consumer has not yet closed the UDP
+    // socket, keep the read buffer filled with errors.
+    if (!binding_) {
+      PepperUDPSocketMessageFilter::SendRecvFromError(PP_ERROR_FAILED);
+      return PP_OK;
+    }
 
-  if (!recvfrom_buffer_.get() && !closed_ && socket_.get()) {
-    DCHECK_EQ(1u, remaining_recv_slots_);
-    DoRecvFrom();
+    remaining_recv_slots_++;
+    socket_->ReceiveMore(1);
   }
 
   return PP_OK;
@@ -364,7 +518,11 @@
   if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &group, &port))
     return PP_ERROR_ADDRESS_INVALID;
 
-  return NetErrorToPepperError(socket_->JoinGroup(net::IPAddress(group)));
+  socket_->JoinGroup(
+      net::IPAddress(group),
+      CreateCompletionCallback<PpapiPluginMsg_UDPSocket_SetOptionReply>(
+          context));
+  return PP_OK_COMPLETIONPENDING;
 }
 
 int32_t PepperUDPSocketMessageFilter::OnMsgLeaveGroup(
@@ -385,308 +543,205 @@
   if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &group, &port))
     return PP_ERROR_ADDRESS_INVALID;
 
-  return NetErrorToPepperError(socket_->LeaveGroup(net::IPAddress(group)));
+  socket_->LeaveGroup(
+      net::IPAddress(group),
+      CreateCompletionCallback<PpapiPluginMsg_UDPSocket_LeaveGroupReply>(
+          context));
+  return PP_OK_COMPLETIONPENDING;
 }
 
-void PepperUDPSocketMessageFilter::DoBind(
+void PepperUDPSocketMessageFilter::DoBindCallback(
+    network::mojom::UDPSocketReceiverRequest receiver_request,
     const ppapi::host::ReplyMessageContext& context,
-    const PP_NetAddress_Private& addr) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+    int result,
+    const base::Optional<net::IPEndPoint>& local_addr_out) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (closed_ || socket_.get()) {
-    SendBindError(context, PP_ERROR_FAILED);
+  if (result != net::OK) {
+    SendBindError(context, NetErrorToPepperError(result));
     return;
   }
 
-  auto socket = std::make_unique<net::UDPSocket>(
-      net::DatagramSocket::DEFAULT_BIND, nullptr, net::NetLogSource());
-
-  net::IPAddressBytes address;
-  uint16_t port;
-  if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
-    SendBindError(context, PP_ERROR_ADDRESS_INVALID);
-    return;
-  }
-  net::IPEndPoint end_point(net::IPAddress(address), port);
-  {
-    int net_result = socket->Open(end_point.GetFamily());
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-
-  if (socket_options_ & SOCKET_OPTION_ADDRESS_REUSE) {
-    int net_result = socket->AllowAddressReuse();
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-  if (socket_options_ & SOCKET_OPTION_BROADCAST) {
-    int net_result = socket->SetBroadcast(true);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-  if (socket_options_ & SOCKET_OPTION_SNDBUF_SIZE) {
-    int net_result = socket->SetSendBufferSize(sndbuf_size_);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-  if (socket_options_ & SOCKET_OPTION_RCVBUF_SIZE) {
-    int net_result = socket->SetReceiveBufferSize(rcvbuf_size_);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-  if (socket_options_ & SOCKET_OPTION_MULTICAST_LOOP) {
-    if (can_use_multicast_ != PP_OK) {
-      SendBindError(context, NetErrorToPepperError(can_use_multicast_));
-      return;
-    }
-
-    int net_result = socket->SetMulticastLoopbackMode(true);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-  if (socket_options_ & SOCKET_OPTION_MULTICAST_TTL) {
-    if (can_use_multicast_ != PP_OK) {
-      SendBindError(context, NetErrorToPepperError(can_use_multicast_));
-      return;
-    }
-
-    int net_result = socket->SetMulticastTimeToLive(multicast_ttl_);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-
-  {
-    int net_result = socket->Bind(end_point);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-
-  net::IPEndPoint bound_address;
-  {
-    int net_result = socket->GetLocalAddress(&bound_address);
-    if (net_result != net::OK) {
-      SendBindError(context, NetErrorToPepperError(net_result));
-      return;
-    }
-  }
-
   PP_NetAddress_Private net_address = NetAddressPrivateImpl::kInvalidNetAddress;
-  if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
-          bound_address.address().bytes(), bound_address.port(),
-          &net_address)) {
+  if (!local_addr_out || !NetAddressPrivateImpl::IPEndPointToNetAddress(
+                             local_addr_out->address().bytes(),
+                             local_addr_out->port(), &net_address)) {
     SendBindError(context, PP_ERROR_ADDRESS_INVALID);
     return;
   }
 
+  if (socket_options_ & SOCKET_OPTION_BROADCAST) {
+    socket_->SetBroadcast(
+        true, base::BindOnce(
+                  &PepperUDPSocketMessageFilter::OnBroadcastConfiguredAfterBind,
+                  base::Unretained(this), std::move(receiver_request), context,
+                  *local_addr_out, net_address));
+    return;
+  }
+
+  // The default value of disabled is fine, if SOCKET_OPTION_BROADCAST is not
+  // set.
+  OnBroadcastConfiguredAfterBind(std::move(receiver_request), context,
+                                 *local_addr_out, net_address, net::OK);
+}
+
+void PepperUDPSocketMessageFilter::OnBroadcastConfiguredAfterBind(
+    network::mojom::UDPSocketReceiverRequest receiver_request,
+    const ppapi::host::ReplyMessageContext& context,
+    const net::IPEndPoint& ip_endpoint,
+    const PP_NetAddress_Private& net_address,
+    int net_result) {
+  if (net_result != net::OK) {
+    SendBindError(context, NetErrorToPepperError(net_result));
+    return;
+  }
+
 #if defined(OS_CHROMEOS)
-  OpenFirewallHole(
-      bound_address,
-      base::Bind(&PepperUDPSocketMessageFilter::OnBindComplete, this,
-                 base::Passed(&socket), context, net_address));
-#else
-  OnBindComplete(std::move(socket), context, net_address);
-#endif
+  pepper_socket_utils::OpenUDPFirewallHole(
+      ip_endpoint,
+      base::BindRepeating(&PepperUDPSocketMessageFilter::OnFirewallHoleOpened,
+                          firewall_hole_weak_ptr_factory_.GetWeakPtr(),
+                          base::Passed(std::move(receiver_request)), context,
+                          net_address));
+#else   // !defined(OS_CHROMEOS)
+  OnBindComplete(std::move(receiver_request), context, net_address);
+#endif  // !defined(OS_CHROMEOS)
 }
 
 void PepperUDPSocketMessageFilter::OnBindComplete(
-    std::unique_ptr<net::UDPSocket> socket,
+    network::mojom::UDPSocketReceiverRequest receiver_request,
     const ppapi::host::ReplyMessageContext& context,
     const PP_NetAddress_Private& net_address) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  socket_.swap(socket);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(socket_);
+
   SendBindReply(context, PP_OK, net_address);
 
-  DoRecvFrom();
+  binding_.Bind(std::move(receiver_request));
+  binding_.set_connection_error_handler(base::BindOnce(
+      &PepperUDPSocketMessageFilter::PipeClosed, base::Unretained(this)));
+  socket_.set_connection_error_handler(base::BindOnce(
+      &PepperUDPSocketMessageFilter::PipeClosed, base::Unretained(this)));
+  socket_->ReceiveMore(UDPSocketResourceConstants::kPluginReceiveBufferSlots);
 }
 
 #if defined(OS_CHROMEOS)
-void PepperUDPSocketMessageFilter::OpenFirewallHole(
-    const net::IPEndPoint& local_address,
-    base::Closure bind_complete) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  pepper_socket_utils::FirewallHoleOpenCallback callback = base::Bind(
-      &PepperUDPSocketMessageFilter::OnFirewallHoleOpened, this, bind_complete);
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&pepper_socket_utils::OpenUDPFirewallHole, local_address,
-                     callback));
-}
-
 void PepperUDPSocketMessageFilter::OnFirewallHoleOpened(
-    base::Closure bind_complete,
+    network::mojom::UDPSocketReceiverRequest receiver_request,
+    const ppapi::host::ReplyMessageContext& context,
+    const PP_NetAddress_Private& net_address,
     std::unique_ptr<chromeos::FirewallHole> hole) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   LOG_IF(WARNING, !hole.get()) << "Firewall hole could not be opened.";
   firewall_hole_.reset(hole.release());
 
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, bind_complete);
+  OnBindComplete(std::move(receiver_request), context, net_address);
 }
 #endif  // defined(OS_CHROMEOS)
 
-void PepperUDPSocketMessageFilter::DoRecvFrom() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(!closed_);
-  DCHECK(socket_.get());
-  DCHECK(!recvfrom_buffer_.get());
-  DCHECK_GT(remaining_recv_slots_, 0u);
-
-  recvfrom_buffer_ =
-      new net::IOBuffer(UDPSocketResourceConstants::kMaxReadSize);
-
-  // Use base::Unretained(this), so that the lifespan of this object doesn't
-  // have to last until the callback is called.
-  // It is safe to do so because |socket_| is owned by this object. If this
-  // object gets destroyed (and so does |socket_|), the callback won't be
-  // called.
-  int net_result = socket_->RecvFrom(
-      recvfrom_buffer_.get(), UDPSocketResourceConstants::kMaxReadSize,
-      &recvfrom_address_,
-      base::BindOnce(&PepperUDPSocketMessageFilter::OnRecvFromCompleted,
-                     base::Unretained(this)));
-  if (net_result != net::ERR_IO_PENDING)
-    OnRecvFromCompleted(net_result);
-}
-
-void PepperUDPSocketMessageFilter::DoSendTo(
-    const ppapi::host::ReplyMessageContext& context,
-    const std::string& data,
-    const PP_NetAddress_Private& addr) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(socket_.get());
-
-  if (closed_ || !socket_.get()) {
-    SendSendToError(context, PP_ERROR_FAILED);
-    return;
-  }
-
-  size_t num_bytes = data.size();
-  if (num_bytes == 0 ||
-      num_bytes >
-          static_cast<size_t>(UDPSocketResourceConstants::kMaxWriteSize)) {
-    // Size of |data| is checked on the plugin side.
-    NOTREACHED();
-    SendSendToError(context, PP_ERROR_BADARGUMENT);
-    return;
-  }
-
-  net::IPAddressBytes address;
-  uint16_t port;
-  if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(addr, &address, &port)) {
-    SendSendToError(context, PP_ERROR_ADDRESS_INVALID);
-    return;
-  }
-
-  scoped_refptr<net::IOBufferWithSize> buffer(
-      new net::IOBufferWithSize(num_bytes));
-  memcpy(buffer->data(), data.data(), num_bytes);
-
-  // Make sure a malicious plugin can't queue up an unlimited number of buffers.
-  size_t num_pending_sends = pending_sends_.size();
-  if (num_pending_sends == UDPSocketResourceConstants::kPluginSendBufferSlots) {
-    SendSendToError(context, PP_ERROR_FAILED);
-    return;
-  }
-
-  pending_sends_.push(
-      PendingSend(net::IPAddress(address), port, buffer, context));
-  // If there are other sends pending, we can't start yet.
-  if (num_pending_sends)
-    return;
-  int net_result = StartPendingSend();
-  if (net_result != net::ERR_IO_PENDING)
-    FinishPendingSend(net_result);
-}
-
-int PepperUDPSocketMessageFilter::StartPendingSend() {
+void PepperUDPSocketMessageFilter::StartPendingSend() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!pending_sends_.empty());
+  DCHECK(socket_);
+
+  net::NetworkTrafficAnnotationTag annotation =
+      net::DefineNetworkTrafficAnnotation("pepper_udp_socket_message_filter",
+                                          R"(
+        semantics {
+          sender: "Chrome Plugin UDP Socket API"
+          description:
+            "Chrome plugins can use this API to send and receive data over the "
+            "network using UDP connections."
+          trigger: "A request from a plugin."
+          data: "Any data that the plugin sends."
+          destination: OTHER
+          destination_other:
+            "Data can be sent to any destination."
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "The only remaining plugin is Flash, so disabling it in Content "
+            "settings will prevent all use of UDP sockets by plugins."
+          policy_exception_justification:
+            "There is no single policy related to plugin use of UDP sockets, "
+            "but there are a number of policies that allow disabling plugins."
+        })");
+
   const PendingSend& pending_send = pending_sends_.front();
   // See OnMsgRecvFrom() for the reason why we use base::Unretained(this)
   // when calling |socket_| methods.
-  int net_result = socket_->SendTo(
-      pending_send.buffer.get(), pending_send.buffer->size(),
+  socket_->SendTo(
       net::IPEndPoint(pending_send.address, pending_send.port),
+      pending_send.data, net::MutableNetworkTrafficAnnotationTag(annotation),
       base::BindOnce(&PepperUDPSocketMessageFilter::OnSendToCompleted,
                      base::Unretained(this)));
-  return net_result;
 }
 
 void PepperUDPSocketMessageFilter::Close() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (socket_.get() && !closed_)
-    socket_->Close();
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  socket_.reset();
+  binding_.Close();
   closed_ = true;
 }
 
-void PepperUDPSocketMessageFilter::OnRecvFromCompleted(int net_result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(recvfrom_buffer_.get());
+void PepperUDPSocketMessageFilter::OnReceived(
+    int result,
+    const base::Optional<net::IPEndPoint>& src_addr,
+    base::Optional<base::span<const uint8_t>> data) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(!closed_);
 
-  int32_t pp_result = NetErrorToPepperError(net_result);
+  int32_t pp_result = NetErrorToPepperError(result);
 
   // Convert IPEndPoint we get back from RecvFrom to a PP_NetAddress_Private
   // to send back.
   PP_NetAddress_Private addr = NetAddressPrivateImpl::kInvalidNetAddress;
-  if (pp_result >= 0 &&
-      !NetAddressPrivateImpl::IPEndPointToNetAddress(
-          recvfrom_address_.address().bytes(), recvfrom_address_.port(),
-          &addr)) {
+  if (pp_result == PP_OK &&
+      (!src_addr ||
+       !NetAddressPrivateImpl::IPEndPointToNetAddress(
+           src_addr->address().bytes(), src_addr->port(), &addr))) {
     pp_result = PP_ERROR_ADDRESS_INVALID;
   }
 
-  if (pp_result >= 0) {
-    SendRecvFromResult(PP_OK, std::string(recvfrom_buffer_->data(), pp_result),
-                       addr);
+  if (pp_result == PP_OK) {
+    std::string data_string;
+    if (data) {
+      data_string = std::string(reinterpret_cast<const char*>(data->data()),
+                                data->size());
+    }
+    SendRecvFromResult(PP_OK, data_string, addr);
   } else {
     SendRecvFromError(pp_result);
   }
 
-  recvfrom_buffer_ = nullptr;
-
-  DCHECK_GT(remaining_recv_slots_, 0u);
-  remaining_recv_slots_--;
-
-  if (remaining_recv_slots_ > 0 && !closed_ && socket_.get())
-    DoRecvFrom();
+  // This should always be the case, but best to protect against a broken /
+  // taken over network service.
+  if (remaining_recv_slots_ > 0)
+    remaining_recv_slots_--;
 }
 
 void PepperUDPSocketMessageFilter::OnSendToCompleted(int net_result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FinishPendingSend(net_result);
 
-  // Start pending sends until none are left or a send doesn't complete.
-  while (!pending_sends_.empty()) {
-    net_result = StartPendingSend();
-    if (net_result == net::ERR_IO_PENDING)
-      break;
-    FinishPendingSend(net_result);
-  }
+  if (!pending_sends_.empty())
+    StartPendingSend();
 }
 
 void PepperUDPSocketMessageFilter::FinishPendingSend(int net_result) {
   DCHECK(!pending_sends_.empty());
   const PendingSend& pending_send = pending_sends_.front();
   int32_t pp_result = NetErrorToPepperError(net_result);
-  if (pp_result < 0)
+  if (pp_result < 0) {
     SendSendToError(pending_send.context, pp_result);
-  else
-    SendSendToReply(pending_send.context, PP_OK, pp_result);
+  } else {
+    // The cast should be safe because of the
+    // UDPSocketResourceConstants::kMaxSendBufferSize before enqueuing the send.
+    SendSendToReply(pending_send.context, PP_OK,
+                    static_cast<int>(pending_send.data.size()));
+  }
 
   pending_sends_.pop();
 }
@@ -707,6 +762,19 @@
     int32_t result,
     const std::string& data,
     const PP_NetAddress_Private& addr) {
+  // Unlike SendReply, which is safe to call on any thread, SendUnsolicitedReply
+  // calls are only safe to make on the IO thread.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(
+          &PepperUDPSocketMessageFilter::SendRecvFromResultOnIOThread, this,
+          result, data, addr));
+}
+
+void PepperUDPSocketMessageFilter::SendRecvFromResultOnIOThread(
+    int32_t result,
+    const std::string& data,
+    const PP_NetAddress_Private& addr) {
   if (resource_host()) {
     resource_host()->host()->SendUnsolicitedReply(
         resource_host()->pp_resource(),
@@ -726,6 +794,12 @@
 void PepperUDPSocketMessageFilter::SendBindError(
     const ppapi::host::ReplyMessageContext& context,
     int32_t result) {
+  socket_.reset();
+#if defined(OS_CHROMEOS)
+  // In the unlikely case that this is due to a Mojo error while trying to open
+  // a hole in the firewall on ChromeOS, abandon opening a hole in the firewall.
+  firewall_hole_weak_ptr_factory_.InvalidateWeakPtrs();
+#endif  // !defined(OS_CHROMEOS)
   SendBindReply(context, result, NetAddressPrivateImpl::kInvalidNetAddress);
 }
 
@@ -741,6 +815,19 @@
   SendSendToReply(context, result, 0);
 }
 
+void PepperUDPSocketMessageFilter::PipeClosed() {
+  Close();
+
+  while (!pending_sends_.empty())
+    FinishPendingSend(PP_ERROR_FAILED);
+
+  // Any reads should fail, after a pipe error.
+  while (remaining_recv_slots_ > 0) {
+    --remaining_recv_slots_;
+    SendRecvFromError(PP_ERROR_FAILED);
+  }
+}
+
 int32_t PepperUDPSocketMessageFilter::CanUseMulticastAPI(
     const PP_NetAddress_Private& addr) {
   // Check for plugin permissions.
@@ -758,4 +845,29 @@
   return PP_OK;
 }
 
+template <class ReturnMessage>
+base::OnceCallback<void(int result)>
+PepperUDPSocketMessageFilter::CreateCompletionCallback(
+    const ppapi::host::HostMessageContext* context) {
+  std::unique_ptr<int> result = std::make_unique<int>(net::ERR_FAILED);
+  int* result_ptr = result.get();
+  base::ScopedClosureRunner closure_runner(
+      base::BindOnce(&PepperUDPSocketMessageFilter::ReturnResult<ReturnMessage>,
+                     base::Unretained(this), context->MakeReplyMessageContext(),
+                     base::Passed(std::move(result))));
+  return base::BindOnce(
+      [](base::ScopedClosureRunner closure_runner, int* result_ptr,
+         int net_result) { *result_ptr = net_result; },
+      std::move(closure_runner), result_ptr);
+}
+
+template <class ReturnMessage>
+void PepperUDPSocketMessageFilter::ReturnResult(
+    const ppapi::host::ReplyMessageContext& context,
+    std::unique_ptr<int> result) {
+  ppapi::host::ReplyMessageContext reply_context(context);
+  reply_context.params.set_result(NetErrorToPepperError(*result));
+  SendReply(reply_context, ReturnMessage());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h
index 3712183..9fc0b14 100644
--- a/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h
+++ b/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h
@@ -6,26 +6,29 @@
 #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_UDP_SOCKET_MESSAGE_FILTER_H_
 
 #include <stddef.h>
+#include <stdint.h>
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "content/public/common/process_type.h"
-#include "net/base/completion_callback.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
-#include "net/socket/udp_socket.h"
 #include "ppapi/c/pp_instance.h"
 #include "ppapi/c/pp_stdint.h"
 #include "ppapi/c/ppb_udp_socket.h"
 #include "ppapi/host/resource_message_filter.h"
+#include "services/network/public/mojom/udp_socket.mojom.h"
 
 struct PP_NetAddress_Private;
 
@@ -34,11 +37,6 @@
 #include "content/public/browser/browser_thread.h"
 #endif  // defined(OS_CHROMEOS)
 
-namespace net {
-class IOBuffer;
-class IOBufferWithSize;
-}
-
 namespace ppapi {
 
 class SocketOptionData;
@@ -48,17 +46,32 @@
 }
 }
 
+namespace network {
+namespace mojom {
+class NetworkContext;
+}
+}  // namespace network
+
 namespace content {
 
 class BrowserPpapiHostImpl;
 
 class CONTENT_EXPORT PepperUDPSocketMessageFilter
-    : public ppapi::host::ResourceMessageFilter {
+    : public ppapi::host::ResourceMessageFilter,
+      public network::mojom::UDPSocketReceiver {
  public:
   PepperUDPSocketMessageFilter(BrowserPpapiHostImpl* host,
                                PP_Instance instance,
                                bool private_api);
 
+  using CreateUDPSocketCallback = base::RepeatingCallback<void(
+      network::mojom::NetworkContext* network_context,
+      network::mojom::UDPSocketRequest socket_request,
+      network::mojom::UDPSocketReceiverPtr socket_receiver)>;
+
+  static void SetCreateUDPSocketCallbackForTesting(
+      const CreateUDPSocketCallback* create_udp_socket_callback);
+
   static size_t GetNumInstances();
 
  protected:
@@ -77,18 +90,19 @@
   struct PendingSend {
     PendingSend(const net::IPAddress& address,
                 int port,
-                const scoped_refptr<net::IOBufferWithSize>& buffer,
+                std::vector<uint8_t> data,
                 const ppapi::host::ReplyMessageContext& context);
     PendingSend(const PendingSend& other);
     ~PendingSend();
 
     net::IPAddress address;
     int port;
-    scoped_refptr<net::IOBufferWithSize> buffer;
+    std::vector<uint8_t> data;
     ppapi::host::ReplyMessageContext context;
   };
 
   // ppapi::host::ResourceMessageFilter overrides.
+  void OnFilterDestroyed() override;
   scoped_refptr<base::TaskRunner> OverrideTaskRunnerForMessage(
       const IPC::Message& message) override;
   int32_t OnResourceMessageReceived(
@@ -111,25 +125,34 @@
   int32_t OnMsgLeaveGroup(const ppapi::host::HostMessageContext* context,
                           const PP_NetAddress_Private& addr);
 
-  void DoBind(const ppapi::host::ReplyMessageContext& context,
-              const PP_NetAddress_Private& addr);
-  void OnBindComplete(std::unique_ptr<net::UDPSocket> socket,
+  void DoBindCallback(network::mojom::UDPSocketReceiverRequest receiver_request,
+                      const ppapi::host::ReplyMessageContext& context,
+                      int result,
+                      const base::Optional<net::IPEndPoint>& local_addr_out);
+  void OnBroadcastConfiguredAfterBind(
+      network::mojom::UDPSocketReceiverRequest receiver_request,
+      const ppapi::host::ReplyMessageContext& context,
+      const net::IPEndPoint& ip_endpoint,
+      const PP_NetAddress_Private& net_address,
+      int net_result);
+  void OnBindComplete(network::mojom::UDPSocketReceiverRequest receiver_request,
                       const ppapi::host::ReplyMessageContext& context,
                       const PP_NetAddress_Private& net_address);
 #if defined(OS_CHROMEOS)
-  void OpenFirewallHole(const net::IPEndPoint& local_address,
-                        base::Closure bind_complete);
-  void OnFirewallHoleOpened(base::Closure bind_complete,
-                            std::unique_ptr<chromeos::FirewallHole> hole);
+  void OnFirewallHoleOpened(
+      network::mojom::UDPSocketReceiverRequest receiver_request,
+      const ppapi::host::ReplyMessageContext& context,
+      const PP_NetAddress_Private& net_address,
+      std::unique_ptr<chromeos::FirewallHole> hole);
 #endif  // defined(OS_CHROMEOS)
-  void DoRecvFrom();
-  void DoSendTo(const ppapi::host::ReplyMessageContext& context,
-                const std::string& data,
-                const PP_NetAddress_Private& addr);
-  int StartPendingSend();
+  void StartPendingSend();
   void Close();
 
-  void OnRecvFromCompleted(int net_result);
+  // network::mojom::UDPSocketReceiver override:
+  void OnReceived(int result,
+                  const base::Optional<net::IPEndPoint>& src_addr,
+                  base::Optional<base::span<const uint8_t>> data) override;
+
   void OnSendToCompleted(int net_result);
   void FinishPendingSend(int net_result);
 
@@ -139,6 +162,9 @@
   void SendRecvFromResult(int32_t result,
                           const std::string& data,
                           const PP_NetAddress_Private& addr);
+  void SendRecvFromResultOnIOThread(int32_t result,
+                                    const std::string& data,
+                                    const PP_NetAddress_Private& addr);
   void SendSendToReply(const ppapi::host::ReplyMessageContext& context,
                        int32_t result,
                        int32_t bytes_written);
@@ -148,9 +174,18 @@
   void SendRecvFromError(int32_t result);
   void SendSendToError(const ppapi::host::ReplyMessageContext& context,
                        int32_t result);
+  void PipeClosed();
 
   int32_t CanUseMulticastAPI(const PP_NetAddress_Private& addr);
 
+  template <class ReturnMessage>
+  base::OnceCallback<void(int result)> CreateCompletionCallback(
+      const ppapi::host::HostMessageContext* context);
+
+  template <class ReturnMessage>
+  void ReturnResult(const ppapi::host::ReplyMessageContext& context,
+                    std::unique_ptr<int> result);
+
   // Bitwise-or of SocketOption flags. This stores the state about whether
   // each option is set before Bind() is called.
   int socket_options_;
@@ -163,20 +198,10 @@
   int multicast_ttl_;
   int32_t can_use_multicast_;
 
-  std::unique_ptr<net::UDPSocket> socket_;
   bool closed_;
-#if defined(OS_CHROMEOS)
-  std::unique_ptr<chromeos::FirewallHole,
-                  content::BrowserThread::DeleteOnUIThread>
-      firewall_hole_;
-#endif  // defined(OS_CHROMEOS)
-
-  scoped_refptr<net::IOBuffer> recvfrom_buffer_;
 
   base::queue<PendingSend> pending_sends_;
 
-  net::IPEndPoint recvfrom_address_;
-
   size_t remaining_recv_slots_;
 
   bool external_plugin_;
@@ -187,6 +212,27 @@
 
   const bool is_potentially_secure_plugin_context_;
 
+  // Bound (in a Mojo sense) when binding (in a network sense) starts. Closed in
+  // Close() and on Mojo pipe errors. Must only be accessed (and destroyed) on
+  // UI thread.
+  network::mojom::UDPSocketPtr socket_;
+
+  // Bound (in a Mojo sense) when binding (in a network sense) completes.
+  // Binding late avoids receiving data when still setting up the socket. Closed
+  // in Close() and on Mojo pipe errors. Must only be accessed (and destroyed)
+  // on UI thread.
+  mojo::Binding<network::mojom::UDPSocketReceiver> binding_;
+
+#if defined(OS_CHROMEOS)
+  std::unique_ptr<chromeos::FirewallHole,
+                  content::BrowserThread::DeleteOnUIThread>
+      firewall_hole_;
+  // Allows for cancellation of opening a hole in the firewall in the case the
+  // network service crashes.
+  base::WeakPtrFactory<PepperUDPSocketMessageFilter>
+      firewall_hole_weak_ptr_factory_;
+#endif  // defined(OS_CHROMEOS)
+
   DISALLOW_COPY_AND_ASSIGN(PepperUDPSocketMessageFilter);
 };
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 172cc4b..ff2723e 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -2323,7 +2323,7 @@
 }
 
 void RenderWidgetHostViewAura::OnDidNavigateMainFrameToNewPage() {
-  ui::GestureRecognizer::Get()->CancelActiveTouches(window_);
+  window_->env()->gesture_recognizer()->CancelActiveTouches(window_);
 }
 
 const viz::FrameSinkId& RenderWidgetHostViewAura::GetFrameSinkId() const {
diff --git a/content/browser/resources/media/client_renderer.js b/content/browser/resources/media/client_renderer.js
index a8eeda4..5d04e38 100644
--- a/content/browser/resources/media/client_renderer.js
+++ b/content/browser/resources/media/client_renderer.js
@@ -373,7 +373,7 @@
         var label = document.createElement('label');
 
         var name_text = p.url || 'Player ' + player.id;
-        var name_node = document.createElement('span');
+        var name_node = document.createElement('div');
         name_node.appendChild(document.createTextNode(name_text));
         name_node.className = 'player-name';
         label.appendChild(name_node);
@@ -385,8 +385,7 @@
           frame.push(p.frame_url);
         var frame_text = frame.join(' - ');
         if (frame_text) {
-          label.appendChild(document.createElement('br'));
-          var frame_node = document.createElement('span');
+          var frame_node = document.createElement('div');
           frame_node.className = 'player-frame';
           frame_node.appendChild(document.createTextNode(frame_text));
           label.appendChild(frame_node);
@@ -405,8 +404,7 @@
           desc.push('(' + p.event + ')');
         var desc_text = desc.join(' ');
         if (desc_text) {
-          label.appendChild(document.createElement('br'));
-          var desc_node = document.createElement('span');
+          var desc_node = document.createElement('div');
           desc_node.className = 'player-desc';
           desc_node.appendChild(document.createTextNode(desc_text));
           label.appendChild(desc_node);
diff --git a/content/browser/resources/media/media_internals.css b/content/browser/resources/media/media_internals.css
index c0545227..a86c5c9 100644
--- a/content/browser/resources/media/media_internals.css
+++ b/content/browser/resources/media/media_internals.css
@@ -180,7 +180,7 @@
 
 label.audio-focus-session,
 label.selectable-button {
-  display: inline-block;
+  display: block;
   border: solid 1px #999;
   border-radius: 3px;
   padding: 6px;
@@ -220,6 +220,9 @@
 
 .player-frame {
   font-style: italic;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
 .no-players-selected #players .property-wrapper,
diff --git a/content/browser/scheduler/responsiveness/watcher_unittest.cc b/content/browser/scheduler/responsiveness/watcher_unittest.cc
index 4ee5f61..c80be85 100644
--- a/content/browser/scheduler/responsiveness/watcher_unittest.cc
+++ b/content/browser/scheduler/responsiveness/watcher_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/pending_task.h"
 #include "base/run_loop.h"
 #include "base/synchronization/lock.h"
+#include "build/build_config.h"
 #include "content/browser/scheduler/responsiveness/calculator.h"
 #include "content/browser/scheduler/responsiveness/native_event_observer.h"
 #include "content/public/browser/browser_thread.h"
@@ -197,7 +198,13 @@
   scoped_refptr<FakeWatcher> watcher_;
 };
 
-TEST_F(ResponsivenessWatcherRealIOThreadTest, MessageLoopObserver) {
+// Flaky on Linux TSAN. https://crbug.com/876561
+#if defined(OS_LINUX) && defined(THREAD_SANITIZER)
+#define MAYBE_MessageLoopObserver DISABLED_MessageLoopObserver
+#else
+#define MAYBE_MessageLoopObserver MessageLoopObserver
+#endif
+TEST_F(ResponsivenessWatcherRealIOThreadTest, MAYBE_MessageLoopObserver) {
   // Post a do-nothing task onto the UI thread.
   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                    base::BindOnce([]() {}));
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 2173838..e63547d 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -694,10 +694,10 @@
                               .AppendASCII("Code Cache");
       }
 
-      // TODO(crbug.com/867552): Currently it is set to an arbitary value.
-      // Update it based on data. Also add a mechanism similar to QuotaSettings
-      // to let embedders control the size of this cache.
-      constexpr int kSizeInBytes = 10 * 1024 * 1024;
+      // TODO(crbug.com/867552): Currently we set it to 0 and let the disk_cache
+      // backend selects the size based on some heuristics. Add support to let
+      // the embedder override the default value.
+      constexpr int kSizeInBytes = 0;
       partition->GetGeneratedCodeCacheContext()->Initialize(code_cache_path,
                                                             kSizeInBytes);
     }
diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc
index d37bc91f..4bcc645b 100644
--- a/content/browser/web_package/signed_exchange_handler.cc
+++ b/content/browser/web_package/signed_exchange_handler.cc
@@ -323,8 +323,7 @@
   int result = cert_verifier->Verify(
       net::CertVerifier::RequestParams(
           unverified_cert_chain_->cert(), envelope_->request_url().host(),
-          0 /* cert_verify_flags */, unverified_cert_chain_->ocsp(),
-          net::CertificateList()),
+          0 /* cert_verify_flags */, unverified_cert_chain_->ocsp()),
       &cert_verify_result_,
       base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete,
                           base::Unretained(this)),
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index f5f7638..3025e35 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -663,8 +663,15 @@
           Focus::kDoCheck);
       return;
     case device::FidoReturnCode::kUserConsentButCredentialNotRecognized:
+      // TODO(crbug/876109): This isn't strictly unreachable.
       NOTREACHED();
       return;
+    case device::FidoReturnCode::kUserConsentDenied:
+      InvokeCallbackAndCleanup(
+          std::move(make_credential_response_callback_),
+          blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
+          Focus::kDoCheck);
+      return;
     case device::FidoReturnCode::kSuccess:
       DCHECK(response_data.has_value());
       request_delegate_->UpdateLastTransportUsed(transport_used);
@@ -768,8 +775,14 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
       return;
     case device::FidoReturnCode::kUserConsentButCredentialExcluded:
+      // TODO(crbug/876109): This isn't strictly unreachable.
       NOTREACHED();
       return;
+    case device::FidoReturnCode::kUserConsentDenied:
+      InvokeCallbackAndCleanup(
+          std::move(get_assertion_response_callback_),
+          blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr);
+      return;
     case device::FidoReturnCode::kSuccess:
       DCHECK(response_data.has_value());
       request_delegate_->UpdateLastTransportUsed(transport_used);
diff --git a/content/public/browser/notification_event_dispatcher.h b/content/public/browser/notification_event_dispatcher.h
index 57afa43c..9015b09 100644
--- a/content/public/browser/notification_event_dispatcher.h
+++ b/content/public/browser/notification_event_dispatcher.h
@@ -11,13 +11,13 @@
 #include "base/optional.h"
 #include "base/strings/string16.h"
 #include "content/common/content_export.h"
-#include "content/public/common/persistent_notification_status.h"
 
 class GURL;
 
 namespace content {
 
 class BrowserContext;
+enum class PersistentNotificationStatus;
 
 // This is the dispatcher to be used for firing events related to notifications.
 // This class is a singleton, the instance of which can be retrieved using the
diff --git a/content/public/common/persistent_notification_status.h b/content/public/common/persistent_notification_status.h
index 38d1dab4..235f9ea 100644
--- a/content/public/common/persistent_notification_status.h
+++ b/content/public/common/persistent_notification_status.h
@@ -9,26 +9,29 @@
 
 // Delivery status for persistent notification clicks to a Service Worker.
 // PersistentNotificationStatus entries should not be reordered or removed.
-enum PersistentNotificationStatus {
+enum class PersistentNotificationStatus {
   // The notificationclick event has been delivered successfully.
-  PERSISTENT_NOTIFICATION_STATUS_SUCCESS = 0,
+  kSuccess = 0,
 
   // The event could not be delivered because the Service Worker is unavailable.
-  PERSISTENT_NOTIFICATION_STATUS_NO_SERVICE_WORKER,
+  kServiceWorkerMissing = 1,
 
   // The event could not be delivered because of a Service Worker error.
-  PERSISTENT_NOTIFICATION_STATUS_SERVICE_WORKER_ERROR,
+  kServiceWorkerError = 2,
 
   // The event has been delivered, but the developer extended the event with a
   // promise that has been rejected.
-  PERSISTENT_NOTIFICATION_STATUS_EVENT_WAITUNTIL_REJECTED,
+  kWaitUntilRejected = 3,
 
   // The event could not be delivered because the data associated with the
   // notification could not be read from the database.
-  PERSISTENT_NOTIFICATION_STATUS_DATABASE_ERROR,
+  kDatabaseError = 4,
 
-  // Only add new entries above this line.
-  PERSISTENT_NOTIFICATION_STATUS_MAX
+  // The event could not be delivered because no permission had been granted to
+  // the origin.
+  kPermissionMissing = 5,
+
+  kMaxValue = kPermissionMissing
 };
 
 }  // content
diff --git a/content/public/test/ppapi_test_utils.cc b/content/public/test/ppapi_test_utils.cc
index 45035f9d..f74bb7a7 100644
--- a/content/public/test/ppapi_test_utils.cc
+++ b/content/public/test/ppapi_test_utils.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "build/build_config.h"
+#include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_switches.h"
 #include "ppapi/shared_impl/ppapi_constants.h"
@@ -144,4 +145,10 @@
   return RegisterPlugins(command_line, plugins);
 }
 
+void SetPepperUDPSocketCallackForTesting(
+    const CreateUDPSocketCallback* create_udp_socket_callback) {
+  content::PepperUDPSocketMessageFilter::SetCreateUDPSocketCallbackForTesting(
+      create_udp_socket_callback);
+}
+
 }  // namespace ppapi
diff --git a/content/public/test/ppapi_test_utils.h b/content/public/test/ppapi_test_utils.h
index 59b3533..cbe1361 100644
--- a/content/public/test/ppapi_test_utils.h
+++ b/content/public/test/ppapi_test_utils.h
@@ -5,13 +5,21 @@
 #ifndef CONTENT_PUBLIC_TEST_PPAPI_TEST_UTILS_H_
 #define CONTENT_PUBLIC_TEST_PPAPI_TEST_UTILS_H_
 
+#include "base/callback_forward.h"
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
+#include "services/network/public/mojom/udp_socket.mojom.h"
 
 namespace base {
 class CommandLine;
 }
 
+namespace network {
+namespace mojom {
+class NetworkContext;
+}
+}  // namespace network
+
 // This file specifies utility functions used in Pepper testing in
 // browser_tests and content_browsertests.
 namespace ppapi {
@@ -35,6 +43,17 @@
 bool RegisterBlinkTestPlugin(base::CommandLine* command_line)
     WARN_UNUSED_RESULT;
 
+using CreateUDPSocketCallback = base::RepeatingCallback<void(
+    network::mojom::NetworkContext* network_context,
+    network::mojom::UDPSocketRequest socket_request,
+    network::mojom::UDPSocketReceiverPtr socket_receiver)>;
+
+// Sets callback to be invoked when creating a UDPSocket for use by pepper.
+// Passed in callback must remain valid until the method is called again with
+// a nullptr, to clear the callback.
+void SetPepperUDPSocketCallackForTesting(
+    const CreateUDPSocketCallback* create_udp_socket_callback);
+
 }  // namespace ppapi
 
 #endif  // CONTENT_PUBLIC_TEST_PPAPI_TEST_UTILS_H_
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e11b184..06e6406 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -732,6 +732,8 @@
     "../browser/device_sensors/device_sensor_browsertest.cc",
     "../browser/devtools/devtools_video_consumer_browsertest.cc",
     "../browser/devtools/protocol/devtools_protocol_browsertest.cc",
+    "../browser/devtools/protocol/devtools_protocol_test_support.cc",
+    "../browser/devtools/protocol/devtools_protocol_test_support.h",
     "../browser/devtools/render_frame_devtools_agent_host_browsertest.cc",
     "../browser/devtools/site_per_process_devtools_browsertest.cc",
     "../browser/display_cutout/display_cutout_browsertest.cc",
diff --git a/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt b/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt
new file mode 100644
index 0000000..adbf886
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-overflow-expected-blink.txt
@@ -0,0 +1,38 @@
+rootWebArea scrollXMin=0
+++paragraph
+++++staticText name='no overflow'
+++++++inlineTextBox name='no overflow'
+++genericContainer
+++++paragraph
+++++++staticText name='tiny'
+++++++++inlineTextBox name='tiny'
+++genericContainer scrollXMin=0
+++++paragraph
+++++++staticText name='x=hidden'
+++++++++inlineTextBox name='x=hidden'
+++genericContainer scrollXMin=0
+++++paragraph
+++++++staticText name='x=auto'
+++++++++inlineTextBox name='x=auto'
+++genericContainer scrollXMin=0
+++++paragraph
+++++++staticText name='x=scroll'
+++++++++inlineTextBox name='x=scroll'
+++paragraph
+++++staticText name='x=visible'
+++++++inlineTextBox name='x=visible'
+++genericContainer scrollXMin=0
+++++paragraph
+++++++staticText name='y=hidden'
+++++++++inlineTextBox name='y=hidden'
+++genericContainer scrollXMin=0
+++++paragraph
+++++++staticText name='y=auto'
+++++++++inlineTextBox name='y=auto'
+++genericContainer scrollXMin=0
+++++paragraph
+++++++staticText name='y=scroll'
+++++++++inlineTextBox name='y=scroll'
+++paragraph
+++++staticText name='y=visible'
+++++++inlineTextBox name='y=visible'
diff --git a/content/test/data/accessibility/html/scrollable-overflow.html b/content/test/data/accessibility/html/scrollable-overflow.html
new file mode 100644
index 0000000..7a2ab9d
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-overflow.html
@@ -0,0 +1,53 @@
+<!--
+* Test scrollXMin -- the presence of this indicates something is scrollable.
+@BLINK-ALLOW:scrollXMin=*
+-->
+<html>
+  <style>
+  body { font-style: monospace; width: 999px;}
+  .x-overflow > div { width: 25px; height: 50px;}
+  .x-overflow > div > p { margin: 0; }  
+  .y-overflow > div { height: 25px; }
+  .y-overflow > div > p { padding: 12px 0; margin: 0;}
+  </style>
+  <body>
+    <div class="no-overflow">  <!-- Not scrollable -->
+      <p>no overflow
+    </div>
+
+    <div class="not-enough-content-to-scroll">  <!-- Not scrollable -->
+      <div style="overflow: scroll; width: 100px; height: 100px">
+        <p>tiny
+    </div>
+
+    <div class="x-overflow">
+      <div style="overflow-x: hidden;">  <!-- Scrollable -->
+          <p>x=hidden 
+      </div>
+      <div style="overflow-x: auto;">  <!-- Scrollable -->
+          <p>x=auto
+      </div>
+      <div style="overflow-x: scroll;">  <!-- Scrollable -->
+          <p>x=scroll
+      </div>
+      <div style="overflow-x: visible;">  <!-- Not scrollable -->
+          <p>x=visible
+      </div>
+    </div>
+
+    <div class="y-overflow">
+      <div style="overflow-y: hidden;">  <!-- Scrollable -->
+          <p>y=hidden
+      </div>
+      <div style="overflow-y: auto;">  <!-- Scrollable -->
+          <p>y=auto
+      </div>
+      <div style="overflow-y: scroll;">  <!-- Scrollable -->
+          <p>y=scroll
+      </div>
+      <div style="overflow-y: visible;">  <!-- Not scrollable -->
+          <p>y=visible
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/content/test/data/accessibility/html/scrollable-textarea-expected-blink.txt b/content/test/data/accessibility/html/scrollable-textarea-expected-blink.txt
new file mode 100644
index 0000000..e1a8186
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-textarea-expected-blink.txt
@@ -0,0 +1,32 @@
+rootWebArea scrollXMin=0
+++genericContainer
+++++textField multiline value='little'
+++++++genericContainer
+++++++++staticText name='little'
+++++++++++inlineTextBox name='little'
+++++textField multiline value='lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text' scrollXMin=0
+++++++genericContainer
+++++++++staticText name='lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text'
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text+'
+++++++++++inlineTextBox name=' '
+++++++++++inlineTextBox name='lots+of+text'
diff --git a/content/test/data/accessibility/html/scrollable-textarea.html b/content/test/data/accessibility/html/scrollable-textarea.html
new file mode 100644
index 0000000..4d662b3
--- /dev/null
+++ b/content/test/data/accessibility/html/scrollable-textarea.html
@@ -0,0 +1,15 @@
+<!--
+* Test scrollXMin -- the presence of this indicates something is scrollable.
+@BLINK-ALLOW:scrollXMin=*
+-->
+<html>
+  <style>
+  body { font-style: monospace; width: 999px;}
+  </style>
+  <body>
+    <!-- Not scrollable -->
+    <textarea cols="14" rows="2">little</textarea>
+    <!-- Scrollable -->
+    <textarea cols="14" rows="2">lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text+ lots+of+text</textarea>        
+  </body>
+</html>
diff --git a/content/test/data/font/dwrite_font_cache_arial.dat b/content/test/data/font/dwrite_font_cache_arial.dat
deleted file mode 100644
index 979b760..0000000
--- a/content/test/data/font/dwrite_font_cache_arial.dat
+++ /dev/null
Binary files differ
diff --git a/content/test/data/font/dwrite_font_cache_corrupt.dat b/content/test/data/font/dwrite_font_cache_corrupt.dat
deleted file mode 100644
index b2c6a293..0000000
--- a/content/test/data/font/dwrite_font_cache_corrupt.dat
+++ /dev/null
Binary files differ
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index 2d30aad..b3eb419 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -27,6 +27,8 @@
   // authenticator), but none of the provided credentials were recognized by
   // the authenticator.
   kUserConsentButCredentialNotRecognized,
+  // The user explicitly refused to provide consent.
+  kUserConsentDenied,
 };
 
 enum class ProtocolVersion {
diff --git a/device/fido/fido_request_handler.h b/device/fido/fido_request_handler.h
index 5492a83..af81cf7 100644
--- a/device/fido/fido_request_handler.h
+++ b/device/fido/fido_request_handler.h
@@ -59,7 +59,8 @@
     }
 
     const auto return_code = ConvertDeviceResponseCodeToFidoReturnCode(
-        device_response_code, response_data.has_value());
+        device_response_code, response_data.has_value(),
+        authenticator->AuthenticatorTransport());
 
     // Any authenticator response codes that do not result from user consent
     // imply that the authenticator should be dropped and that other on-going
@@ -81,7 +82,8 @@
   static base::Optional<FidoReturnCode>
   ConvertDeviceResponseCodeToFidoReturnCode(
       CtapDeviceResponseCode device_response_code,
-      bool response_has_value) {
+      bool response_has_value,
+      FidoTransportProtocol transport) {
     switch (device_response_code) {
       case CtapDeviceResponseCode::kSuccess:
         return response_has_value
@@ -97,6 +99,22 @@
       case CtapDeviceResponseCode::kCtap2ErrNoCredentials:
         return FidoReturnCode::kUserConsentButCredentialNotRecognized;
 
+      // The user explicitly denied the operation.
+      case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
+        // TODO(crbug/875982): Clarify if |CTAP2_ERR_OPERATION_DENIED| is "a
+        // status indicating that the user cancelled the operation" in the
+        // spirit of https://www.w3.org/TR/webauthn/, sections 5.1.3 and
+        // 5.1.4.. The CTAP2 spec also uses it for authenticator-chosen
+        // timeouts, so it is a little unclear.
+        //
+        // For Touch ID, we know it means that the operation failed during (or
+        // after) user verification, so we do the translation the internal
+        // transport only.
+        return transport == FidoTransportProtocol::kInternal
+                   ? base::make_optional<FidoReturnCode>(
+                         FidoReturnCode::kUserConsentDenied)
+                   : base::nullopt;
+
       // For all other errors, the authenticator will be dropped, and other
       // authenticators may continue.
       default:
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index e700915b..427ae00 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -43,6 +43,7 @@
   kSuccess = 0x00,
   kErrorReceivedAfterObtainingUserPresence = 0x01,
   kProcessingError = 0x02,
+  kOperationDenied = 0x03,
 };
 
 class TestTransportAvailabilityObserver
@@ -117,6 +118,10 @@
                                  std::vector<uint8_t>());
         return;
 
+      case FakeTaskResponse::kOperationDenied:
+        std::move(callback_).Run(
+            CtapDeviceResponseCode::kCtap2ErrOperationDenied, base::nullopt);
+        return;
       case FakeTaskResponse::kProcessingError:
       default:
         std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
@@ -182,6 +187,10 @@
   return {base::strict_cast<uint8_t>(FakeTaskResponse::kProcessingError)};
 }
 
+std::vector<uint8_t> CreateFakeOperationDeniedError() {
+  return {base::strict_cast<uint8_t>(FakeTaskResponse::kOperationDenied)};
+}
+
 }  // namespace
 
 class FidoRequestHandlerTest : public ::testing::Test {
@@ -389,6 +398,67 @@
             callback().status());
 }
 
+// If a device with transport type kInternal returns a
+// CTAP2_ERR_OPERATION_DENIED error, the request should be cancelled on all
+// pending authenticators.
+TEST_F(FidoRequestHandlerTest,
+       TestRequestWithOperationDeniedErrorInternalTransport) {
+  auto request_handler = CreateFakeHandler();
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  // Device will send CTAP2_ERR_OPERATION_DENIED.
+  auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation(
+      test_data::kTestGetInfoResponsePlatformDevice);
+  device0->SetDeviceTransport(FidoTransportProtocol::kInternal);
+  device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+                                       CreateFakeOperationDeniedError(),
+                                       base::TimeDelta::FromMicroseconds(10));
+
+  auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
+  EXPECT_CALL(*device1, Cancel());
+
+  discovery()->AddDevice(std::move(device0));
+  discovery()->AddDevice(std::move(device1));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  callback().WaitForCallback();
+  EXPECT_TRUE(request_handler->is_complete());
+  EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
+}
+
+// Like |TestRequestWithOperationDeniedErrorInternalTransport|, but the
+// CTAP2_ERR_OPERATION_DENIED error is returned by a device with
+// cross-platform transport. The operation should not be cancelled (see
+// https://crbug/875982).
+TEST_F(FidoRequestHandlerTest,
+       TestRequestWithOperationDeniedErrorCrossPlatform) {
+  auto request_handler = CreateFakeHandler();
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  // Device will send CTAP2_ERR_OPERATION_DENIED.
+  auto device0 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device0->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
+  device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+                                       CreateFakeOperationDeniedError());
+
+  // Pending device will *NOT* be cancelled and can send a success reply
+  // eventually.
+  auto device1 = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
+                                       CreateFakeSuccessDeviceResponse(),
+                                       base::TimeDelta::FromMicroseconds(10));
+
+  discovery()->AddDevice(std::move(device0));
+  discovery()->AddDevice(std::move(device1));
+
+  // The request will stay pending, the reply has not triggered the callback.
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  callback().WaitForCallback();
+  EXPECT_TRUE(request_handler->is_complete());
+  EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+}
+
 // Requests should be dispatched to the authenticator passed to
 // SetPlatformAuthenticatorOrMarkUnavailable.
 TEST_F(FidoRequestHandlerTest, TestSetPlatformAuthenticator) {
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index cdc8c1ef..7460e3c9 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -586,4 +586,54 @@
   EXPECT_FALSE(get_assertion_callback().was_called());
 }
 
+// If a device with transport type kInternal returns a
+// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
+// FidoReturnCode::kUserConsentDenied. Pending authenticators should be
+// cancelled.
+TEST_F(FidoGetAssertionHandlerTest,
+       TestRequestWithOperationDeniedErrorPlatform) {
+  auto platform_device = MockFidoDevice::MakeCtap(
+      ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+  platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+  platform_device->ExpectCtap2CommandAndRespondWithError(
+      CtapRequestCommand::kAuthenticatorGetAssertion,
+      CtapDeviceResponseCode::kCtap2ErrOperationDenied,
+      base::TimeDelta::FromMicroseconds(10));
+  EXPECT_CALL(*platform_device, GetId())
+      .WillRepeatedly(testing::Return("device0"));
+  set_mock_platform_device(std::move(platform_device));
+
+  auto other_device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  other_device->ExpectCtap2CommandAndDoNotRespond(
+      CtapRequestCommand::kAuthenticatorGetAssertion);
+  EXPECT_CALL(*other_device, Cancel);
+
+  auto request_handler = CreateGetAssertionHandlerCtap();
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  discovery()->AddDevice(std::move(other_device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(get_assertion_callback().was_called());
+  EXPECT_EQ(FidoReturnCode::kUserConsentDenied,
+            get_assertion_callback().status());
+}
+
+// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
+// cross-platform device. The request should not complete after the
+// CTAP2_ERR_OPERATION_DENIED error (see https://crbug/875982).
+TEST_F(FidoGetAssertionHandlerTest,
+       TestRequestWithOperationDeniedErrorCrossPlatform) {
+  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device->ExpectCtap2CommandAndRespondWithError(
+      CtapRequestCommand::kAuthenticatorGetAssertion,
+      CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+
+  auto request_handler = CreateGetAssertionHandlerCtap();
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
 }  // namespace device
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index e51a7a4e..122d777 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -512,4 +512,56 @@
   EXPECT_FALSE(callback().was_called());
 }
 
+// If a device with transport type kInternal returns a
+// CTAP2_ERR_OPERATION_DENIED error, the request should complete with
+// FidoReturnCode::kUserConsentDenied.
+TEST_F(FidoMakeCredentialHandlerTest,
+       TestRequestWithOperationDeniedErrorPlatform) {
+  auto platform_device = MockFidoDevice::MakeCtap(
+      ReadCTAPGetInfoResponse(test_data::kTestGetInfoResponsePlatformDevice));
+  platform_device->SetDeviceTransport(FidoTransportProtocol::kInternal);
+  platform_device->ExpectCtap2CommandAndRespondWithError(
+      CtapRequestCommand::kAuthenticatorMakeCredential,
+      CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+  EXPECT_CALL(*platform_device, GetId())
+      .WillRepeatedly(testing::Return("device0"));
+  set_mock_platform_device(std::move(platform_device));
+
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kPlatform,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_TRUE(callback().was_called());
+  EXPECT_EQ(FidoReturnCode::kUserConsentDenied, callback().status());
+}
+
+// Like |TestRequestWithOperationDeniedErrorPlatform|, but with a
+// cross-platform device. The request should not complete after the
+// CTAP2_ERR_OPERATION_DENIED error (see https://crbug/875982).
+TEST_F(FidoMakeCredentialHandlerTest,
+       TestRequestWithOperationDeniedErrorCrossPlatform) {
+  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
+  device->ExpectCtap2CommandAndRespondWithError(
+      CtapRequestCommand::kAuthenticatorMakeCredential,
+      CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
+
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
 }  // namespace device
diff --git a/device/fido/mock_fido_device.cc b/device/fido/mock_fido_device.cc
index 27673f9..27030a5 100644
--- a/device/fido/mock_fido_device.cc
+++ b/device/fido/mock_fido_device.cc
@@ -105,7 +105,7 @@
   static size_t i = 0;
   EXPECT_CALL(*this, GetId())
       .WillRepeatedly(
-          testing::Return(base::StrCat({"mockdevice", std::to_string(i)})));
+          testing::Return(base::StrCat({"mockdevice", std::to_string(i++)})));
 }
 
 void MockFidoDevice::ExpectCtap2CommandAndRespondWith(
@@ -122,6 +122,14 @@
       .WillOnce(::testing::WithArg<1>(::testing::Invoke(send_response)));
 }
 
+void MockFidoDevice::ExpectCtap2CommandAndRespondWithError(
+    CtapRequestCommand command,
+    CtapDeviceResponseCode response_code,
+    base::TimeDelta delay) {
+  std::array<uint8_t, 1> data{base::strict_cast<uint8_t>(response_code)};
+  return ExpectCtap2CommandAndRespondWith(std::move(command), data, delay);
+}
+
 void MockFidoDevice::ExpectRequestAndRespondWith(
     base::span<const uint8_t> request,
     base::Optional<base::span<const uint8_t>> response,
diff --git a/device/fido/mock_fido_device.h b/device/fido/mock_fido_device.h
index 5aae859..2337341 100644
--- a/device/fido/mock_fido_device.h
+++ b/device/fido/mock_fido_device.h
@@ -76,6 +76,10 @@
       CtapRequestCommand command,
       base::Optional<base::span<const uint8_t>> response,
       base::TimeDelta delay = base::TimeDelta());
+  void ExpectCtap2CommandAndRespondWithError(
+      CtapRequestCommand command,
+      CtapDeviceResponseCode response_code,
+      base::TimeDelta delay = base::TimeDelta());
   void ExpectRequestAndRespondWith(
       base::span<const uint8_t> request,
       base::Optional<base::span<const uint8_t>> response,
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn
index 972caf9d6..b4fb58b 100644
--- a/extensions/browser/api/BUILD.gn
+++ b/extensions/browser/api/BUILD.gn
@@ -149,13 +149,11 @@
   }
 }
 
-json_schema_api("api_registration") {
-  sources = extensions_api_schema_files
+function_registration("api_registration") {
+  sources = extensions_api_schema_files + extensions_api_uncompiled_sources
   impl_dir = "//extensions/browser/api"
-  bundle_registration = true
   bundle_name = ""
   root_namespace = extensions_api_root_namespace
-  uncompiled_sources = extensions_api_uncompiled_sources
 
   deps = [
     ":api",
diff --git a/extensions/common/api/BUILD.gn b/extensions/common/api/BUILD.gn
index efdedc8..46d3230 100644
--- a/extensions/common/api/BUILD.gn
+++ b/extensions/common/api/BUILD.gn
@@ -13,14 +13,11 @@
 
 # TODO(devlin): Enforce visibility restrictions on more of these targets?
 
-json_schema_api("generated_api_bundles") {
-  sources = extensions_api_schema_files
-  bundle = true
+generated_json_strings("generated_api_json_strings") {
+  sources = extensions_api_schema_files + extensions_api_uncompiled_sources +
+            extensions_api_uncompiled_bundle_schema_sources
   bundle_name = ""
   root_namespace = extensions_api_root_namespace
-  uncompiled_sources = extensions_api_uncompiled_sources
-  uncompiled_bundle_schema_sources =
-      extensions_api_uncompiled_bundle_schema_sources
   deps = [
     "//base",
     "//extensions/buildflags",
@@ -80,7 +77,7 @@
 
 group("api") {
   public_deps = [
-    ":generated_api_bundles",
+    ":generated_api_json_strings",
     ":generated_api_types",
     ":mojom",
     "//extensions/buildflags",
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h
index 8f3def7b..80ccf19 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.h
@@ -60,7 +60,7 @@
  protected:
   // Returns the frame which is embedding the corresponding plugin element.
   virtual content::RenderFrame* GetEmbedderRenderFrame() const;
-  virtual void CreateMimeHandlerViewGuestIfNecessary() = 0;
+  virtual void CreateMimeHandlerViewGuestIfNecessary();
   virtual blink::WebFrame* GetGuestProxyFrame() const = 0;
   virtual int32_t GetInstanceId() const = 0;
   virtual gfx::Size GetElementSize() const = 0;
diff --git a/extensions/shell/common/api/BUILD.gn b/extensions/shell/common/api/BUILD.gn
index beb3d0eb..70d335ef 100644
--- a/extensions/shell/common/api/BUILD.gn
+++ b/extensions/shell/common/api/BUILD.gn
@@ -14,10 +14,9 @@
 schema_sources = [ "identity.idl" ]
 root_namespace = "extensions::shell::api::%(namespace)s"
 
-json_schema_api("generated_api_registration_bundle") {
+function_registration("generated_api_registration") {
   sources = schema_sources
   impl_dir = "//extensions/shell/browser/api"
-  bundle_registration = true
   bundle_name = "Shell"
 
   deps = [
@@ -27,11 +26,9 @@
   visibility = [ ":api" ]
 }
 
-json_schema_api("generated_api_strings_bundle") {
+generated_json_strings("generated_api_json_strings") {
   sources = schema_sources
-  bundle = true
   bundle_name = "Shell"
-
   visibility = [ ":api" ]
 }
 
@@ -53,8 +50,8 @@
 
 group("api") {
   public_deps = [
-    ":generated_api_registration_bundle",
-    ":generated_api_strings_bundle",
+    ":generated_api_json_strings",
+    ":generated_api_registration",
     ":generated_api_types",
   ]
 }
diff --git a/gpu/config/gpu_driver_bug_list.cc b/gpu/config/gpu_driver_bug_list.cc
index 184d855..6e828f7 100644
--- a/gpu/config/gpu_driver_bug_list.cc
+++ b/gpu/config/gpu_driver_bug_list.cc
@@ -95,12 +95,15 @@
 // static
 void GpuDriverBugList::AppendAllWorkarounds(
     std::vector<const char*>* workarounds) {
-  workarounds->resize(workarounds->size() +
-                      NUMBER_OF_GPU_DRIVER_BUG_WORKAROUND_TYPES);
+  static_assert(std::extent<decltype(kFeatureList)>::value ==
+                    NUMBER_OF_GPU_DRIVER_BUG_WORKAROUND_TYPES,
+                "Expected kFeatureList to include all gpu workarounds");
 
-#define GPU_OP(type, name) workarounds->push_back(#name);
-  GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP)
-#undef GPU_OP
+  DCHECK(workarounds->empty());
+  workarounds->resize(NUMBER_OF_GPU_DRIVER_BUG_WORKAROUND_TYPES);
+  size_t i = 0;
+  for (const GpuDriverBugWorkaroundInfo& feature : kFeatureList)
+    (*workarounds)[i++] = feature.name;
 }
 
 // static
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index e93a992..4d4b380 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -39,7 +39,11 @@
       builders { name: "android_clang_dbg_recipe" }
       builders { name: "android_compile_dbg" }
       builders { name: "android_cronet" }
-      builders { name: "android-kitkat-arm-rel" }
+      # TODO(crbug.com/876539): Reenable this.
+      builders {
+        experiment_percentage: 100
+        name: "android-kitkat-arm-rel"
+      }
       builders { name: "android-marshmallow-arm64-rel" }
       builders { name: "cast_shell_android" }
       builders { name: "cast_shell_linux" }
diff --git a/ios/build/bots/chromium.fyi/ios-device-goma-canary-clobber.json b/ios/build/bots/chromium.fyi/ios-device-goma-canary-clobber.json
index 3d76236..cf87c6775 100644
--- a/ios/build/bots/chromium.fyi/ios-device-goma-canary-clobber.json
+++ b/ios/build/bots/chromium.fyi/ios-device-goma-canary-clobber.json
@@ -22,5 +22,6 @@
   "tests": [
   ],
   "clobber": true,
-  "use_goma_canary": true
+  "use_goma_canary": true,
+  "goma_client_type": "candidate"
 }
diff --git a/ios/build/tools/convert_gn_xcodeproj.py b/ios/build/tools/convert_gn_xcodeproj.py
index 0b465e0..d8dc806 100755
--- a/ios/build/tools/convert_gn_xcodeproj.py
+++ b/ios/build/tools/convert_gn_xcodeproj.py
@@ -8,7 +8,7 @@
 GN generates Xcode projects that build one configuration only. However, typical
 iOS development involves using the Xcode IDE to toggle the platform and
 configuration. This script replaces the 'gn' configuration with 'Debug',
-'Release' and 'Profile', and changes the ninja invokation to honor these
+'Release' and 'Profile', and changes the ninja invocation to honor these
 configurations.
 """
 
@@ -27,9 +27,6 @@
 import tempfile
 
 
-XCTEST_PRODUCT_TYPE = 'com.apple.product-type.bundle.unit-test'
-
-
 class XcodeProject(object):
 
   def __init__(self, objects, counter = 0):
@@ -91,37 +88,12 @@
   for value in project.objects.values():
     isa = value['isa']
 
-    # TODO(crbug.com/619072): gn does not write the min deployment target in the
-    # generated Xcode project, so add it while doing the conversion, only if it
-    # is not present. Remove this code and comment once the bug is fixed and gn
-    # has rolled past it.
-    if isa == 'XCBuildConfiguration':
-      build_settings = value['buildSettings']
-      if 'IPHONEOS_DEPLOYMENT_TARGET' not in build_settings:
-        build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
-
     # Teach build shell script to look for the configuration and platform.
     if isa == 'PBXShellScriptBuildPhase':
       value['shellScript'] = value['shellScript'].replace(
           'ninja -C .',
           'ninja -C "../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"')
 
-    # Configure BUNDLE_LOADER and TEST_HOST for xctest targets (if not yet
-    # configured by gn). Old convention was to name the test dynamic module
-    # "foo" and the host "foo_host" while the new convention is to name the
-    # test "foo_module" and the host "foo". Decide which convention to use
-    # by inspecting the target name.
-    if isa == 'PBXNativeTarget' and value['productType'] == XCTEST_PRODUCT_TYPE:
-      configuration_list = project.objects[value['buildConfigurationList']]
-      for config_name in configuration_list['buildConfigurations']:
-        config = project.objects[config_name]
-        if not config['buildSettings'].get('BUNDLE_LOADER'):
-          assert value['name'].endswith('_module')
-          host_name = value['name'][:-len('_module')]
-          config['buildSettings']['BUNDLE_LOADER'] = '$(TEST_HOST)'
-          config['buildSettings']['TEST_HOST'] = \
-              '${BUILT_PRODUCTS_DIR}/%s.app/%s' % (host_name, host_name)
-
     # Add new configuration, using the first one as default.
     if isa == 'XCConfigurationList':
       value['defaultConfigurationName'] = configurations[0]
diff --git a/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm b/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
index 7f9c20ce..fd1d9f6 100644
--- a/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
+++ b/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
@@ -245,7 +245,7 @@
       completionHandler:nil];
   NSString* const kActiveElementNameJS = @"document.activeElement.name";
   EXPECT_NSEQ(@"firstname",
-              web::ExecuteJavaScript(manager_, kActiveElementNameJS));
+              web::test::ExecuteJavaScript(manager_, kActiveElementNameJS));
   [manager_ selectNextElement];
   NSString* activeElementNameJS = GetActiveElementName();
   if (shouldSkip)
diff --git a/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm b/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm
index 3dbf1f38..2841041b 100644
--- a/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm
+++ b/ios/chrome/browser/translate/js_language_detection_manager_unittest.mm
@@ -59,7 +59,7 @@
   // Injects JS, waits for the completion handler and verifies if the result
   // was what was expected.
   void InjectJsAndVerify(NSString* js, id expected_result) {
-    EXPECT_NSEQ(expected_result, web::ExecuteJavaScript(manager_, js));
+    EXPECT_NSEQ(expected_result, web::test::ExecuteJavaScript(manager_, js));
   }
 
   // Injects JS, and spins the run loop until |condition| block returns true
@@ -232,7 +232,7 @@
       initWithFormat:
           @"__gCrWeb.languageDetection.getTextContent(document.body, %lu);",
           language_detection::kMaxIndexChars];
-  NSString* result = web::ExecuteJavaScript(manager_, script);
+  NSString* result = web::test::ExecuteJavaScript(manager_, script);
   EXPECT_EQ(language_detection::kMaxIndexChars, [result length]);
 }
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index ff23885..5136d3f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -16,9 +16,6 @@
     "//ios/chrome/browser/autofill/manual_fill:manual_fill",
     "//ios/chrome/browser/ui/autofill/manual_fill:manual_fill_ui",
     "//net:net",
-    "//third_party/material_design_icons:ic_account_circle",
-    "//third_party/material_design_icons:ic_credit_card",
-    "//third_party/material_design_icons:ic_vpn_key",
     "//url:url",
   ]
   libs = [ "UIKit.framework" ]
@@ -29,9 +26,15 @@
   sources = [
     "credential.h",
     "credential.mm",
+    "manual_fill_accessory_view_controller.h",
+    "manual_fill_accessory_view_controller.mm",
   ]
   deps = [
     "//base",
+    "//ios/chrome/browser/ui/autofill/manual_fill/resources:addresses",
+    "//third_party/material_design_icons:ic_credit_card",
+    "//third_party/material_design_icons:ic_keyboard",
+    "//third_party/material_design_icons:ic_vpn_key",
   ]
   libs = [ "UIKit.framework" ]
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.h b/ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.h
deleted file mode 100644
index b07f0da..0000000
--- a/ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_KEYBOARD_ACCESSORY_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_KEYBOARD_ACCESSORY_VIEW_H_
-
-#import <UIKit/UIKit.h>
-
-// Protocol to handle user interactions in a ManualFillKeyboardAccessoryView.
-@protocol ManualFillKeyboardAccessoryViewDelegate
-
-// Invoked after the user touches the `accounts` button.
-- (void)accountButtonPressed;
-
-// Invoked after the user touches the `credit cards` button.
-- (void)cardButtonPressed;
-
-// Invoked after the user touches the `passwords` button.
-- (void)passwordButtonPressed;
-
-@end
-
-// This view contains the icons to activate "Manual Fill". It is meant to be
-// shown above the keyboard on iPhone and above the manual fill view.
-@interface ManualFillKeyboardAccessoryView : UIView
-
-// Instances an object with the desired delegate.
-//
-// @param delegate The delegate for this object.
-// @return A fresh object with the passed delegate.
-- (instancetype)initWithDelegate:
-    (id<ManualFillKeyboardAccessoryViewDelegate>)delegate
-    NS_DESIGNATED_INITIALIZER;
-
-// Unavailable. Use `initWithDelegate:`.
-- (instancetype)init NS_UNAVAILABLE;
-
-// Unavailable. Use `initWithDelegate:`.
-- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
-
-// Unavailable. Use `initWithDelegate:`.
-- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
-
-// Unavailable. Use `initWithDelegate:`.
-- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_KEYBOARD_ACCESSORY_VIEW_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.mm b/ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.mm
deleted file mode 100644
index 2f8eec27..0000000
--- a/ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.mm
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/autofill/manual_fill/keyboard_accessory_view.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface ManualFillKeyboardAccessoryView ()
-
-@property(nonatomic, readonly, weak) id<ManualFillKeyboardAccessoryViewDelegate>
-    delegate;
-
-@end
-
-@implementation ManualFillKeyboardAccessoryView
-
-@synthesize delegate = _delegate;
-
-- (instancetype)initWithDelegate:
-    (id<ManualFillKeyboardAccessoryViewDelegate>)delegate {
-  self = [super initWithFrame:CGRectZero];
-  if (self) {
-    _delegate = delegate;
-    UIColor* tintColor = [UIColor colorWithRed:115.0 / 255.0
-                                         green:115.0 / 255.0
-                                          blue:115.0 / 255.0
-                                         alpha:1.0];
-
-    UIButton* passwordButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    UIImage* keyImage = [UIImage imageNamed:@"ic_vpn_key"];
-    [passwordButton setImage:keyImage forState:UIControlStateNormal];
-    passwordButton.tintColor = tintColor;
-    passwordButton.translatesAutoresizingMaskIntoConstraints = NO;
-    [passwordButton addTarget:_delegate
-                       action:@selector(passwordButtonPressed)
-             forControlEvents:UIControlEventTouchUpInside];
-    [self addSubview:passwordButton];
-
-    UIButton* cardsButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    UIImage* cardImage = [UIImage imageNamed:@"ic_credit_card"];
-    [cardsButton setImage:cardImage forState:UIControlStateNormal];
-    cardsButton.tintColor = tintColor;
-    cardsButton.translatesAutoresizingMaskIntoConstraints = NO;
-    [cardsButton addTarget:_delegate
-                    action:@selector(cardButtonPressed)
-          forControlEvents:UIControlEventTouchUpInside];
-    [self addSubview:cardsButton];
-
-    UIButton* accountButton = [UIButton buttonWithType:UIButtonTypeCustom];
-    UIImage* accountImage = [UIImage imageNamed:@"ic_account_circle"];
-    [accountButton setImage:accountImage forState:UIControlStateNormal];
-    accountButton.tintColor = tintColor;
-    accountButton.translatesAutoresizingMaskIntoConstraints = NO;
-    [accountButton addTarget:_delegate
-                      action:@selector(accountButtonPressed)
-            forControlEvents:UIControlEventTouchUpInside];
-    [self addSubview:accountButton];
-
-    NSLayoutXAxisAnchor* menuLeadingAnchor = self.leadingAnchor;
-    if (@available(iOS 11, *)) {
-      menuLeadingAnchor = self.safeAreaLayoutGuide.leadingAnchor;
-    }
-
-    [NSLayoutConstraint activateConstraints:@[
-      // Vertical constraints.
-      [passwordButton.heightAnchor constraintEqualToAnchor:self.heightAnchor],
-      [passwordButton.topAnchor constraintEqualToAnchor:self.topAnchor],
-
-      [cardsButton.heightAnchor constraintEqualToAnchor:self.heightAnchor],
-      [cardsButton.topAnchor constraintEqualToAnchor:self.topAnchor],
-
-      [accountButton.heightAnchor constraintEqualToAnchor:self.heightAnchor],
-      [accountButton.topAnchor constraintEqualToAnchor:self.topAnchor],
-
-      // Horizontal constraints.
-      [passwordButton.leadingAnchor constraintEqualToAnchor:menuLeadingAnchor
-                                                   constant:12],
-
-      [cardsButton.leadingAnchor
-          constraintEqualToAnchor:passwordButton.trailingAnchor
-                         constant:8],
-
-      [accountButton.leadingAnchor
-          constraintEqualToAnchor:cardsButton.trailingAnchor
-                         constant:8],
-    ]];
-  }
-
-  return self;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h
new file mode 100644
index 0000000..51082f7
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_ACCESSORY_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_ACCESSORY_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+// Protocol to handle user interactions in a ManualFillAccessoryViewController.
+@protocol ManualFillAccessoryViewControllerDelegate
+
+// Invoked after the user touches the `accounts` button.
+- (void)accountButtonPressed;
+
+// Invoked after the user touches the `credit cards` button.
+- (void)cardButtonPressed;
+
+// Invoked after the user touches the `keyboard` button.
+- (void)keyboardButtonPressed;
+
+// Invoked after the user touches the `passwords` button.
+- (void)passwordButtonPressed;
+
+@end
+
+// This view contains the icons to activate "Manual Fill". It is meant to be
+// shown above the keyboard on iPhone and above the manual fill view.
+@interface ManualFillAccessoryViewController : UIViewController
+
+// Instances an object with the desired delegate.
+//
+// @param delegate The delegate for this object.
+// @return A fresh object with the passed delegate.
+- (instancetype)initWithDelegate:
+    (id<ManualFillAccessoryViewControllerDelegate>)delegate
+    NS_DESIGNATED_INITIALIZER;
+
+// Unavailable. Use `initWithDelegate:`.
+- (instancetype)initWithNibName:(NSString*)nibNameOrNil
+                         bundle:(NSBundle*)nibBundleOrNil NS_UNAVAILABLE;
+- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
+- (instancetype)init NS_UNAVAILABLE;
+
+// Resets to the original state, with the keyboard icon hidden and no icon
+// selected.
+- (void)reset;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_MANUAL_FILL_MANUAL_FILL_ACCESSORY_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
new file mode 100644
index 0000000..687106b1
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
@@ -0,0 +1,177 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
+
+#import "ios/chrome/browser/ui/autofill/manual_fill/uicolor_manualfill.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+static NSTimeInterval MFAnimationDuration = 0.20;
+
+@interface ManualFillAccessoryViewController ()
+
+@property(nonatomic, readonly, weak)
+    id<ManualFillAccessoryViewControllerDelegate>
+        delegate;
+
+@property(nonatomic, strong) UIButton* keyboardButton;
+@property(nonatomic, strong) UIButton* passwordButton;
+@property(nonatomic, strong) UIButton* cardsButton;
+@property(nonatomic, strong) UIButton* accountButton;
+
+@end
+
+@implementation ManualFillAccessoryViewController
+
+@synthesize delegate = _delegate;
+@synthesize keyboardButton = _keyboardButton;
+@synthesize passwordButton = _passwordButton;
+@synthesize cardsButton = _cardsButton;
+@synthesize accountButton = _accountButton;
+
+- (instancetype)initWithDelegate:
+    (id<ManualFillAccessoryViewControllerDelegate>)delegate {
+  self = [super initWithNibName:nil bundle:nil];
+  if (self) {
+    _delegate = delegate;
+  }
+  return self;
+}
+
+- (void)loadView {
+  self.view = [[UIView alloc] init];
+  self.view.translatesAutoresizingMaskIntoConstraints = NO;
+
+  UIColor* tintColor = [self activeTintColor];
+
+  self.keyboardButton = [UIButton buttonWithType:UIButtonTypeSystem];
+  UIImage* keyboardImage = [UIImage imageNamed:@"ic_keyboard"];
+  [self.keyboardButton setImage:keyboardImage forState:UIControlStateNormal];
+  self.keyboardButton.tintColor = tintColor;
+  self.keyboardButton.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.keyboardButton addTarget:self
+                          action:@selector(keyboardButtonPressed)
+                forControlEvents:UIControlEventTouchUpInside];
+
+  self.passwordButton = [UIButton buttonWithType:UIButtonTypeSystem];
+  UIImage* keyImage = [UIImage imageNamed:@"ic_vpn_key"];
+  [self.passwordButton setImage:keyImage forState:UIControlStateNormal];
+  self.passwordButton.tintColor = tintColor;
+  self.passwordButton.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.passwordButton addTarget:self
+                          action:@selector(passwordButtonPressed)
+                forControlEvents:UIControlEventTouchUpInside];
+
+  self.cardsButton = [UIButton buttonWithType:UIButtonTypeSystem];
+  UIImage* cardImage = [UIImage imageNamed:@"ic_credit_card"];
+  [self.cardsButton setImage:cardImage forState:UIControlStateNormal];
+  self.cardsButton.tintColor = tintColor;
+  self.cardsButton.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.cardsButton addTarget:self
+                       action:@selector(cardButtonPressed)
+             forControlEvents:UIControlEventTouchUpInside];
+
+  self.accountButton = [UIButton buttonWithType:UIButtonTypeSystem];
+  UIImage* accountImage = [UIImage imageNamed:@"addresses"];
+  [self.accountButton setImage:accountImage forState:UIControlStateNormal];
+  self.accountButton.tintColor = tintColor;
+  self.accountButton.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.accountButton addTarget:self
+                         action:@selector(accountButtonPressed)
+               forControlEvents:UIControlEventTouchUpInside];
+
+  NSLayoutXAxisAnchor* menuLeadingAnchor = self.view.leadingAnchor;
+  if (@available(iOS 11, *)) {
+    menuLeadingAnchor = self.view.safeAreaLayoutGuide.leadingAnchor;
+  }
+  NSLayoutXAxisAnchor* menuTrailingAnchor = self.view.trailingAnchor;
+  if (@available(iOS 11, *)) {
+    menuTrailingAnchor = self.view.safeAreaLayoutGuide.trailingAnchor;
+  }
+
+  UIStackView* stackView = [[UIStackView alloc] initWithArrangedSubviews:@[
+    self.keyboardButton, self.passwordButton, self.accountButton,
+    self.cardsButton
+  ]];
+  stackView.spacing = 10;
+  stackView.axis = UILayoutConstraintAxisHorizontal;
+  stackView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:stackView];
+
+  [NSLayoutConstraint activateConstraints:@[
+    // Vertical constraints.
+    [stackView.heightAnchor constraintEqualToAnchor:self.view.heightAnchor],
+    [stackView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+
+    // Horizontal constraints.
+    [stackView.leadingAnchor constraintEqualToAnchor:menuLeadingAnchor
+                                            constant:10],
+    [stackView.trailingAnchor constraintEqualToAnchor:menuTrailingAnchor],
+  ]];
+  self.keyboardButton.hidden = YES;
+  self.keyboardButton.alpha = 0.0;
+}
+
+- (void)reset {
+  [self resetTintColors];
+  self.keyboardButton.hidden = YES;
+  self.keyboardButton.alpha = 0.0;
+}
+
+// Resets the colors of all the icons to the active color.
+- (void)resetTintColors {
+  UIColor* activeTintColor = [self activeTintColor];
+  [self.accountButton setTintColor:activeTintColor];
+  [self.passwordButton setTintColor:activeTintColor];
+  [self.cardsButton setTintColor:activeTintColor];
+}
+
+- (UIColor*)activeTintColor {
+  return [UIColor.blackColor colorWithAlphaComponent:0.5];
+}
+
+- (void)animateKeyboardButtonHidden:(BOOL)hidden {
+  [UIView animateWithDuration:MFAnimationDuration
+                   animations:^{
+                     if (hidden) {
+                       self.keyboardButton.hidden = YES;
+                       self.keyboardButton.alpha = 0.0;
+                     } else {
+                       self.keyboardButton.hidden = NO;
+                       self.keyboardButton.alpha = 1.0;
+                     }
+                   }];
+}
+
+- (void)keyboardButtonPressed {
+  [self animateKeyboardButtonHidden:YES];
+  [self resetTintColors];
+  [self.delegate keyboardButtonPressed];
+}
+
+- (void)passwordButtonPressed {
+  [self animateKeyboardButtonHidden:NO];
+  [self resetTintColors];
+  [self.passwordButton setTintColor:UIColor.cr_manualFillTintColor];
+  [self.delegate passwordButtonPressed];
+}
+
+- (void)cardButtonPressed {
+  [self animateKeyboardButtonHidden:NO];
+  [self resetTintColors];
+  [self.cardsButton setTintColor:UIColor.cr_manualFillTintColor];
+  [self.delegate cardButtonPressed];
+}
+
+- (void)accountButtonPressed {
+  [self animateKeyboardButtonHidden:NO];
+  [self resetTintColors];
+  [self.accountButton setTintColor:UIColor.cr_manualFillTintColor];
+  [self.delegate accountButtonPressed];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 872eca4..e70ec5cb 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -5059,7 +5059,7 @@
   self.consentBumpCoordinator.delegate = self;
   [self.consentBumpCoordinator start];
   self.consentBumpCoordinator.viewController.modalPresentationStyle =
-      UIModalPresentationPageSheet;
+      UIModalPresentationFormSheet;
   [self presentViewController:self.consentBumpCoordinator.viewController
                      animated:YES
                    completion:nil];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
index 6a49120..d282cf33 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -157,9 +157,10 @@
                     safeAreaInsets:(UIEdgeInsets)safeAreaInsets {
   if (self.isShowing && IsUIRefreshPhase1Enabled()) {
     CGFloat progress =
-        self.logoIsShowing
+        self.logoIsShowing || !content_suggestions::IsRegularXRegularSizeClass()
             ? [self.headerView searchFieldProgressForOffset:offset
                                              safeAreaInsets:safeAreaInsets]
+            // RxR with no logo hides the fakebox, so always show the omnibox.
             : 1;
     if (!IsSplitToolbarMode()) {
       [self.toolbarDelegate setScrollProgressForTabletOmnibox:progress];
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index bbf320a..ad9f559 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -264,16 +264,7 @@
 #pragma mark - LocationBarViewControllerDelegate
 
 - (void)locationBarSteadyViewTapped {
-  FullscreenController* fullscreenController =
-      FullscreenControllerFactory::GetInstance()->GetForBrowserState(
-          _browserState);
-  if (fullscreenController->GetProgress() < 1) {
-    // The first tap should exit fullscreen.
-    fullscreenController->ResetModel();
-  } else {
-    // The toolbar is fully visible, focus the omnibox.
-    [self focusOmnibox];
-  }
+  [self focusOmnibox];
 }
 
 - (void)locationBarCopyTapped {
diff --git a/ios/chrome/browser/ui/static_content/static_html_view_controller_unittest.mm b/ios/chrome/browser/ui/static_content/static_html_view_controller_unittest.mm
index 0687cee..0f5aaa05 100644
--- a/ios/chrome/browser/ui/static_content/static_html_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/static_content/static_html_view_controller_unittest.mm
@@ -229,8 +229,8 @@
   ASSERT_OCMOCK_VERIFY((OCMockObject*)loader);
   __block id string_in_page = nil;
   base::test::ios::WaitUntilCondition(^bool {
-    string_in_page =
-        web::ExecuteJavaScript([content webView], @"document.body.innerHTML");
+    string_in_page = web::test::ExecuteJavaScript([content webView],
+                                                  @"document.body.innerHTML");
     return ![string_in_page isEqual:@""];
   });
   EXPECT_TRUE([string_in_page isKindOfClass:[NSString class]]);
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
index d20676a..fa9bc41 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
+++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
@@ -743,13 +743,9 @@
 - (void)updateTabSwitcherGuide {
   NamedGuide* tabSwitcherGuide =
       [NamedGuide guideWithName:kTabStripTabSwitcherGuide view:self.view];
-  tabSwitcherGuide.constrainedFrame = _tabSwitcherButton.frame;
-  if (UseRTLLayout()) {
-    CGRect frame = tabSwitcherGuide.constrainedFrame;
-    frame.origin.x =
-        self.view.frame.size.width - _tabSwitcherButton.frame.origin.x;
-    tabSwitcherGuide.constrainedFrame = frame;
-  }
+  tabSwitcherGuide.constrainedFrame =
+      [_tabSwitcherButton.superview convertRect:_tabSwitcherButton.frame
+                                         toView:tabSwitcherGuide.owningView];
 }
 
 #pragma mark -
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
index 9da648f7..a35817b5 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_coordinator.mm
@@ -155,6 +155,12 @@
                              animated:NO];
 }
 
+- (void)exitFullscreen {
+  FullscreenControllerFactory::GetInstance()
+      ->GetForBrowserState(self.browserState)
+      ->ResetModel();
+}
+
 #pragma mark - FakeboxFocuser
 
 - (void)focusFakebox {
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.h b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.h
index a4adda9..07ef687 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.h
+++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.h
@@ -47,6 +47,9 @@
 // Button to cancel the edit of the location bar.
 @property(nonatomic, strong, readonly) UIButton* cancelButton;
 
+// Button taking the full size of the toolbar. Expands the toolbar when  tapped.
+@property(nonatomic, strong, readonly) UIButton* collapsedToolbarButton;
+
 // Constraints to be activated when the location bar is expanded and positioned
 // relatively to the cancel button.
 @property(nonatomic, strong, readonly)
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.mm b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.mm
index e3a07dbb..754e8ae6 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.mm
@@ -76,6 +76,9 @@
 
 // Button to cancel the edit of the location bar, redefined as readwrite.
 @property(nonatomic, strong, readwrite) UIButton* cancelButton;
+// Button taking the full size of the toolbar. Expands the toolbar when  tapped.
+// Redefined as readwrite.
+@property(nonatomic, strong, readwrite) UIButton* collapsedToolbarButton;
 
 // Constraints for the location bar, redefined as readwrite.
 @property(nonatomic, strong, readwrite)
@@ -112,6 +115,7 @@
 @synthesize bookmarkButton = _bookmarkButton;
 @synthesize toolsMenuButton = _toolsMenuButton;
 @synthesize cancelButton = _cancelButton;
+@synthesize collapsedToolbarButton = _collapsedToolbarButton;
 @synthesize expandedConstraints = _expandedConstraints;
 @synthesize contractedConstraints = _contractedConstraints;
 @synthesize contractedNoMarginConstraints = _contractedNoMarginConstraints;
@@ -143,6 +147,7 @@
   [self setUpCancelButton];
   [self setUpLocationBar];
   [self setUpProgressBar];
+  [self setUpCollapsedToolbarButton];
 
   [self setUpConstraints];
 }
@@ -291,6 +296,14 @@
   [self addSubview:self.progressBar];
 }
 
+// Sets the collapsedToolbarButton up.
+- (void)setUpCollapsedToolbarButton {
+  self.collapsedToolbarButton = [[UIButton alloc] init];
+  self.collapsedToolbarButton.translatesAutoresizingMaskIntoConstraints = NO;
+  self.collapsedToolbarButton.hidden = YES;
+  [self addSubview:self.collapsedToolbarButton];
+}
+
 // Sets the constraints up.
 - (void)setUpConstraints {
   id<LayoutGuideProvider> safeArea = SafeAreaLayoutGuideForView(self);
@@ -408,6 +421,9 @@
     [self.progressBar.heightAnchor
         constraintEqualToConstant:kProgressBarHeight],
   ]];
+
+  // CollapsedToolbarButton constraints.
+  AddSameConstraints(self, self.collapsedToolbarButton);
 }
 
 #pragma mark - Property accessors
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller.mm
index ff9ce7c1..c8a7d8e6 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller.mm
@@ -102,6 +102,10 @@
   // set to topLayoutGuide after the view creation on iOS 10.
   [self.view setUp];
 
+  [self.view.collapsedToolbarButton addTarget:self
+                                       action:@selector(exitFullscreen)
+                             forControlEvents:UIControlEventTouchUpInside];
+
   if (IsCompactHeight(self)) {
     self.view.locationBarExtraBottomPadding.constant =
         kAdaptiveLocationBarExtraVerticalMargin;
@@ -172,6 +176,8 @@
       [self.buttonFactory.toolbarConfiguration
           locationBarBackgroundColorWithVisibility:alphaValue];
   self.previousFullscreenProgress = progress;
+
+  self.view.collapsedToolbarButton.hidden = progress > 0.05;
 }
 
 - (void)updateForFullscreenEnabled:(BOOL)enabled {
@@ -262,4 +268,9 @@
   [NSLayoutConstraint deactivateConstraints:self.view.expandedConstraints];
 }
 
+// Exits fullscreen.
+- (void)exitFullscreen {
+  [self.delegate exitFullscreen];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller_delegate.h b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller_delegate.h
index 0e1503a..376bcaad 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller_delegate.h
@@ -14,6 +14,9 @@
 - (void)viewControllerTraitCollectionDidChange:
     (UITraitCollection*)previousTraitCollection;
 
+// Exits fullscreen.
+- (void)exitFullscreen;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TOOLBAR_ADAPTIVE_PRIMARY_TOOLBAR_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/web/chrome_web_client_unittest.mm b/ios/chrome/browser/web/chrome_web_client_unittest.mm
index fd3680e..76cd615 100644
--- a/ios/chrome/browser/web/chrome_web_client_unittest.mm
+++ b/ios/chrome/browser/web/chrome_web_client_unittest.mm
@@ -102,13 +102,13 @@
 TEST_F(ChromeWebClientTest, WKWebViewEarlyPageScriptAccessibility) {
   // Chrome scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, browser_state());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<ChromeWebClient>());
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForAllFrames(browser_state());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"object", web::ExecuteJavaScript(
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"object", web::test::ExecuteJavaScript(
                              web_view, @"typeof __gCrWeb.accessibility"));
 }
 
@@ -116,28 +116,28 @@
 TEST_F(ChromeWebClientTest, WKWebViewEarlyPageScriptPrint) {
   // Chrome scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, browser_state());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<ChromeWebClient>());
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(browser_state());
-  web::ExecuteJavaScript(web_view, script);
+  web::test::ExecuteJavaScript(web_view, script);
   EXPECT_NSEQ(@"object",
-              web::ExecuteJavaScript(web_view, @"typeof __gCrWeb.print"));
+              web::test::ExecuteJavaScript(web_view, @"typeof __gCrWeb.print"));
 }
 
 // Tests that ChromeWebClient provides autofill controller script for WKWebView.
 TEST_F(ChromeWebClientTest, WKWebViewEarlyPageScriptAutofillController) {
   // Chrome scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, browser_state());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<ChromeWebClient>());
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(browser_state());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"object",
-              web::ExecuteJavaScript(web_view, @"typeof __gCrWeb.autofill"));
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"object", web::test::ExecuteJavaScript(
+                             web_view, @"typeof __gCrWeb.autofill"));
 }
 
 // Tests that ChromeWebClient provides credential manager script for WKWebView
@@ -145,21 +145,21 @@
 TEST_F(ChromeWebClientTest, WKWebViewEarlyPageScriptCredentialManager) {
   // Chrome scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, browser_state());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<ChromeWebClient>());
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(browser_state());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"undefined", web::ExecuteJavaScript(
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"undefined", web::test::ExecuteJavaScript(
                                 web_view, @"typeof navigator.credentials"));
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kCredentialManager);
   script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(browser_state());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"object", web::ExecuteJavaScript(
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"object", web::test::ExecuteJavaScript(
                              web_view, @"typeof navigator.credentials"));
 }
 
@@ -168,15 +168,15 @@
 TEST_F(ChromeWebClientTest, WKWebViewEarlyPageScriptPaymentRequestEnabled) {
   // Chrome scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, browser_state());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<ChromeWebClient>());
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(payments::features::kWebPayments);
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(browser_state());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"function", web::ExecuteJavaScript(
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"function", web::test::ExecuteJavaScript(
                                web_view, @"typeof window.PaymentRequest"));
 }
 
@@ -185,15 +185,15 @@
 TEST_F(ChromeWebClientTest, WKWebViewEarlyPageScriptPaymentRequestDisabled) {
   // Chrome scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, browser_state());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<ChromeWebClient>());
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(payments::features::kWebPayments);
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(browser_state());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"undefined", web::ExecuteJavaScript(
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"undefined", web::test::ExecuteJavaScript(
                                 web_view, @"typeof window.PaymentRequest"));
 }
 
diff --git a/ios/chrome/browser/web/early_page_script_perftest.mm b/ios/chrome/browser/web/early_page_script_perftest.mm
index d6e588ae..52ac53c94 100644
--- a/ios/chrome/browser/web/early_page_script_perftest.mm
+++ b/ios/chrome/browser/web/early_page_script_perftest.mm
@@ -34,7 +34,7 @@
   }
 
   // Injects early script into WKWebView.
-  void InjectEarlyScript() { web::ExecuteJavaScript(web_view_, script_); }
+  void InjectEarlyScript() { web::test::ExecuteJavaScript(web_view_, script_); }
 
   // WKWebView to test scripts injections.
   WKWebView* web_view_;
diff --git a/ios/third_party/gtx/BUILD.gn b/ios/third_party/gtx/BUILD.gn
new file mode 100644
index 0000000..c4b641ba
--- /dev/null
+++ b/ios/third_party/gtx/BUILD.gn
@@ -0,0 +1,58 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ios/ios_sdk.gni")
+import("//build/config/ios/rules.gni")
+
+config("config") {
+  include_dirs = [ "src/Classes" ]
+}
+
+ios_framework_bundle("gtx") {
+  output_name = "GTX"
+  info_plist = "src/FrameworkFiles/Info.plist"
+
+  testonly = true
+  sources = [
+    "src/Classes/GTXAccessibilityTree.h",
+    "src/Classes/GTXAnalytics.h",
+    "src/Classes/GTXAnalyticsUtils.h",
+    "src/Classes/GTXAssertions.h",
+    "src/Classes/GTXCheckBlock.h",
+    "src/Classes/GTXChecking.h",
+    "src/Classes/GTXChecksCollection.h",
+    "src/Classes/GTXCommon.h",
+    "src/Classes/GTXElementBlacklist.h",
+    "src/Classes/GTXErrorReporter.h",
+    "src/Classes/GTXImageAndColorUtils.h",
+    "src/Classes/GTXImageRGBAData.h",
+    "src/Classes/GTXLogging.h",
+    "src/Classes/GTXPluginXCTestCase.h",
+    "src/Classes/GTXTestCase.h",
+    "src/Classes/GTXTestSuite.h",
+    "src/Classes/GTXToolKit.h",
+    "src/Classes/GTXiLib.h",
+    "src/Classes/GTXiLibCore.h",
+    "src/Classes/NSError+GTXAdditions.h",
+  ]
+  public_headers = [ "src/Classes/GTXiLib.h" ]
+
+  libs = [
+    "CoreGraphics.framework",
+    "Foundation.framework",
+    "QuartzCore.framework",
+    "UIKit.framework",
+    "XCTest.framework",
+  ]
+  public_configs = [ ":config" ]
+
+  configs -= [
+    "//build/config/gcc:symbol_visibility_hidden",
+    "//build/config/compiler:chromium_code",
+  ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/gcc:symbol_visibility_default",
+  ]
+}
diff --git a/ios/third_party/gtx/LICENSE b/ios/third_party/gtx/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/ios/third_party/gtx/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/ios/third_party/gtx/OWNERS b/ios/third_party/gtx/OWNERS
new file mode 100644
index 0000000..35e99669
--- /dev/null
+++ b/ios/third_party/gtx/OWNERS
@@ -0,0 +1,2 @@
+lindsayw@google.com
+rohitrao@google.com
\ No newline at end of file
diff --git a/ios/third_party/gtx/README.chromium b/ios/third_party/gtx/README.chromium
new file mode 100644
index 0000000..af5f40f
--- /dev/null
+++ b/ios/third_party/gtx/README.chromium
@@ -0,0 +1,10 @@
+Name: GTXiLib
+Short Name: GTX
+URL: https://github.com/google/GTXiLib
+Version: 1
+License: Apache 2.0
+License File: NOT_SHIPPED
+Security Critical: no
+
+Description:
+Google Toolbox for Accessibility for the iOS platform or simply GTX-eye is a framework for iOS accessibility testing.
\ No newline at end of file
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 3e173aa9..7186635 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -435,6 +435,7 @@
     "//ios/web/web_state:context_menu",
     "//ios/web/web_state:navigation_context",
     "//ios/web/web_state/js",
+    "//ios/web/web_state/js:script_util",
     "//ios/web/web_state/ui:crw_wk_script_message_router",
     "//net:test_support",
     "//services/service_manager/public/cpp",
@@ -479,6 +480,7 @@
     "//ios/web/web_state:navigation_context",
     "//ios/web/web_state:wk_web_view_security_util",
     "//ios/web/web_state/js",
+    "//ios/web/web_state/js:script_util",
     "//ios/web/web_state/ui:crw_context_menu_controller",
     "//ios/web/web_state/ui:crw_wk_script_message_router",
     "//ios/web/web_state/ui:favicon_util",
diff --git a/ios/web/browser_state_web_view_partition_inttest.mm b/ios/web/browser_state_web_view_partition_inttest.mm
index 494066c..89c2c3c 100644
--- a/ios/web/browser_state_web_view_partition_inttest.mm
+++ b/ios/web/browser_state_web_view_partition_inttest.mm
@@ -67,12 +67,12 @@
         stringWithFormat:@"document.cookie='%@=%@;"
                          @"Expires=Tue, 05-May-9999 02:18:23 GMT; Path=/'",
                          key, value];
-    web::ExecuteJavaScript(web_view, set_cookie);
+    web::test::ExecuteJavaScript(web_view, set_cookie);
   }
 
   // Returns a csv list of all cookies from |web_view|.
   NSString* GetCookies(WKWebView* web_view) {
-    id result = web::ExecuteJavaScript(web_view, @"document.cookie");
+    id result = web::test::ExecuteJavaScript(web_view, @"document.cookie");
     return base::mac::ObjCCastStrict<NSString>(result);
   }
 
@@ -83,14 +83,15 @@
     NSString* set_local_storage_item = [NSString
         stringWithFormat:@"localStorage.setItem('%@', '%@')", key, value];
     NSError* unused_error = nil;
-    web::ExecuteJavaScript(web_view, set_local_storage_item, &unused_error);
+    web::test::ExecuteJavaScript(web_view, set_local_storage_item,
+                                 &unused_error);
   }
 
   // Returns the localstorage value associated with |key| from |web_view|.
   id GetLocalStorageItem(NSString* key, WKWebView* web_view) {
     NSString* get_local_storage_value =
         [NSString stringWithFormat:@"localStorage.getItem('%@');", key];
-    return web::ExecuteJavaScript(web_view, get_local_storage_value);
+    return web::test::ExecuteJavaScript(web_view, get_local_storage_value);
   }
 
   // Loads a test web page (that contains a small string) in |web_view| and
diff --git a/ios/web/public/test/BUILD.gn b/ios/web/public/test/BUILD.gn
index 626e046..4f0e9792 100644
--- a/ios/web/public/test/BUILD.gn
+++ b/ios/web/public/test/BUILD.gn
@@ -19,6 +19,7 @@
     "//ios/web/public/test/http_server",
     "//ios/web/test:test_support",
     "//ios/web/web_state:error_translation_util",
+    "//ios/web/web_state/js:script_util",
     "//testing/gtest",
     "//ui/base",
     "//url",
diff --git a/ios/web/public/test/js_test_util.h b/ios/web/public/test/js_test_util.h
index 5b5f3039..c5a2de86 100644
--- a/ios/web/public/test/js_test_util.h
+++ b/ios/web/public/test/js_test_util.h
@@ -14,6 +14,7 @@
 @class CRWJSInjectionReceiver;
 
 namespace web {
+namespace test {
 
 // These functions synchronously execute JavaScript and return result as id.
 // id will be backed up by different classes depending on resulting JS type:
@@ -46,6 +47,11 @@
 // Waits until custom javascript is injected into __gCrWeb.
 bool WaitForInjectedScripts(WKWebView* web_view) WARN_UNUSED_RESULT;
 
+// Returns an autoreleased string containing the JavaScript loaded from a
+// bundled resource file with the given name (excluding extension).
+NSString* GetPageScript(NSString* script_file_name);
+
+}  // namespace test
 }  // namespace web
 
 #endif  // IOS_WEB_PUBLIC_TEST_JS_TEST_UTIL_H_
diff --git a/ios/web/public/test/js_test_util.mm b/ios/web/public/test/js_test_util.mm
index b7af9b7..84db1fd 100644
--- a/ios/web/public/test/js_test_util.mm
+++ b/ios/web/public/test/js_test_util.mm
@@ -7,10 +7,12 @@
 #import <WebKit/WebKit.h>
 
 #include "base/logging.h"
+#include "base/mac/bundle_locations.h"
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#import "ios/web/web_state/js/page_script_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -22,6 +24,7 @@
 using base::test::ios::WaitUntilConditionOrTimeout;
 
 namespace web {
+namespace test {
 
 id ExecuteJavaScript(CRWJSInjectionManager* manager, NSString* script) {
   __block NSString* result;
@@ -94,5 +97,9 @@
   });
 }
 
+NSString* GetPageScript(NSString* script_file_name) {
+  return web::GetPageScript(script_file_name);
+}
+}  // namespace test
 }  // namespace web
 
diff --git a/ios/web/public/test/web_js_test.h b/ios/web/public/test/web_js_test.h
index 6ac43a46..9272c049 100644
--- a/ios/web/public/test/web_js_test.h
+++ b/ios/web/public/test/web_js_test.h
@@ -10,6 +10,7 @@
 #include <memory>
 
 #import "base/mac/bundle_locations.h"
+#import "ios/web/public/test/js_test_util.h"
 #include "ios/web/public/web_client.h"
 #import "testing/gtest_mac.h"
 
@@ -70,13 +71,7 @@
   ASSERT_NSEQ(@"object", WebTestT::ExecuteJavaScript(@"typeof __gCrWeb"));
 
   for (NSString* java_script_path in java_script_paths_) {
-    NSString* path =
-        [base::mac::FrameworkBundle() pathForResource:java_script_path
-                                               ofType:@"js"];
-    WebTestT::ExecuteJavaScript([NSString
-        stringWithContentsOfFile:path
-                        encoding:NSUTF8StringEncoding
-                           error:nil]);
+    WebTestT::ExecuteJavaScript(web::test::GetPageScript(java_script_path));
   }
 }
 
diff --git a/ios/web/test/web_int_test.mm b/ios/web/test/web_int_test.mm
index c139941..727dad95 100644
--- a/ios/web/test/web_int_test.mm
+++ b/ios/web/test/web_int_test.mm
@@ -91,7 +91,8 @@
 }
 
 id WebIntTest::ExecuteJavaScript(NSString* script) {
-  return web::ExecuteJavaScript(web_state()->GetJSInjectionReceiver(), script);
+  return web::test::ExecuteJavaScript(web_state()->GetJSInjectionReceiver(),
+                                      script);
 }
 
 bool WebIntTest::ExecuteBlockAndWaitForLoad(const GURL& url,
diff --git a/ios/web/web_state/js/BUILD.gn b/ios/web/web_state/js/BUILD.gn
index 1f7930e..de76bc5b 100644
--- a/ios/web/web_state/js/BUILD.gn
+++ b/ios/web/web_state/js/BUILD.gn
@@ -6,6 +6,7 @@
 
 source_set("js") {
   deps = [
+    ":script_util",
     "//base",
     "//ios/web/public",
     "//ios/web/web_state/ui:crw_wk_script_message_router",
@@ -18,6 +19,18 @@
     "crw_js_post_request_loader.mm",
     "crw_js_window_id_manager.h",
     "crw_js_window_id_manager.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("script_util") {
+  deps = [
+    "//base",
+    "//ios/web/public",
+  ]
+
+  sources = [
     "page_script_util.h",
     "page_script_util.mm",
   ]
diff --git a/ios/web/web_state/js/context_menu_js_unittest.mm b/ios/web/web_state/js/context_menu_js_unittest.mm
index 420d0c3..7380107 100644
--- a/ios/web/web_state/js/context_menu_js_unittest.mm
+++ b/ios/web/web_state/js/context_menu_js_unittest.mm
@@ -83,10 +83,11 @@
   // Returns details of the DOM element at the given |x| and |y| coordinates.
   // The given point is in the device's coordinate space.
   id FindElementAtPoint(CGFloat x, CGFloat y) {
-    EXPECT_TRUE(web::WaitForInjectedScripts(web_view_));
+    EXPECT_TRUE(web::test::WaitForInjectedScripts(web_view_));
 
     // Force layout
-    web::ExecuteJavaScript(web_view_, @"document.getElementsByTagName('p')");
+    web::test::ExecuteJavaScript(web_view_,
+                                 @"document.getElementsByTagName('p')");
 
     // Clear previous script message response.
     script_message_handler_.lastReceivedScriptMessage = nil;
@@ -114,7 +115,7 @@
         stringWithFormat:@"__gCrWeb.findElementAtPoint('%@', %g, %g, %g, %g)",
                          kRequestId, x, y, GetWebViewContentSize().width,
                          GetWebViewContentSize().height];
-    return web::ExecuteJavaScript(web_view_, script);
+    return web::test::ExecuteJavaScript(web_view_, script);
   }
 
   // Handles script message responses sent from |web_view_|.
@@ -132,7 +133,7 @@
       @"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_value = @{
@@ -150,7 +151,7 @@
        "style='width:200;height:200;' src='file:///bogus'/>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_value = @{
@@ -170,7 +171,7 @@
       @"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(0, 0);
   NSDictionary* expected_value = @{
@@ -195,7 +196,7 @@
       @"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(GetWebViewContentSize().width / 2, 50);
   NSDictionary* expected_value = @{
@@ -223,7 +224,7 @@
        "</a>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, link_image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_value = @{
@@ -245,7 +246,7 @@
        "</a>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, link_image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(0, 0);
   NSDictionary* expected_value = @{
@@ -272,7 +273,7 @@
        "</a>";
 
   NSString* html = [NSString stringWithFormat:kPageContentTemplate, link_image];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(GetWebViewContentSize().width / 2, 50);
   NSDictionary* expected_value = @{
@@ -298,7 +299,7 @@
       @"<a href='%@'><img width=400 height=400 src='foo'></img></a>";
 
   NSString* html = [NSString stringWithFormat:kImageHtml, kLinkDest];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
@@ -318,7 +319,7 @@
        "<img width=400 height=400 src='foo'></img></a>";
 
   // A page with a link with some JavaScript that does not result in a NOP.
-  ASSERT_TRUE(web::LoadHtml(web_view_, kImageHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kImageHtml, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
@@ -337,7 +338,7 @@
   NSString* kImageHtml =
       @"<a href='javascript:;'><img width=400 height=400 src='foo'></img></a>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kImageHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kImageHtml, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
@@ -357,7 +358,7 @@
       @"<a href='javascript:void(0);'>"
        "<img width=400 height=400 src='foo'></img></a>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kImageHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kImageHtml, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
@@ -377,7 +378,7 @@
       @"<a href='javascript:void(0);  void(0); void(0)'>"
        "<img width=400 height=400 src='foo'></img></a>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kImageHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kImageHtml, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   NSDictionary* expected_result = @{
@@ -399,7 +400,7 @@
        "</a>";
 
   NSString* html = [NSString stringWithFormat:kImageHtml, kLinkDest];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(5, 5);
   NSDictionary* expected_result = @{
@@ -427,7 +428,7 @@
        "width:40px;height:40px'/>"
        "</div></body> </html>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kHtml, GetTestURL()));
 
   id result = FindElementAtPoint(10, 10);
   NSDictionary* expected_value = @{
@@ -445,7 +446,8 @@
        "<img width=400 height=400 src='foo'></img>";
 
   // Load the invalid meta tag
-  ASSERT_TRUE(web::LoadHtml(web_view_, kInvalidReferrerTag, GetTestURL()));
+  ASSERT_TRUE(
+      web::test::LoadHtml(web_view_, kInvalidReferrerTag, GetTestURL()));
 
   id result = FindElementAtPoint(20, 20);
   ASSERT_TRUE([result isKindOfClass:[NSDictionary class]]);
@@ -467,10 +469,11 @@
        " <div><a href='http://destination'>link</a></div>"
        "</body></html>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kHtml, GetTestURL()));
 
   // Force layout to ensure |content_height| below is correct.
-  web::ExecuteJavaScript(web_view_, @"document.getElementsByTagName('p')");
+  web::test::ExecuteJavaScript(web_view_,
+                               @"document.getElementsByTagName('p')");
 
   // Scroll the webView to the bottom to make the link accessible.
   CGFloat content_height = GetWebViewContentSize().height;
@@ -505,7 +508,7 @@
   NSString* kLinkHtml = @"<a href='%@'>link</a>";
 
   NSString* html = [NSString stringWithFormat:kLinkHtml, kLinkDest];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
   NSDictionary* expected_result = @{
@@ -534,7 +537,7 @@
        "link</a>";
 
   NSString* html = [NSString stringWithFormat:kLinkHtml, kLinkDest];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
   NSDictionary* expected_result = @{
@@ -554,7 +557,7 @@
       @"<a href='http://destination' "
        "style='-webkit-touch-callout:none;'>link</a>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kLinkHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kLinkHtml, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
   EXPECT_NSEQ(@{kContextMenuElementRequestId : kRequestId}, result);
@@ -569,7 +572,7 @@
        " <a href='http://destination'>link</a>"
        "</body>";
 
-  ASSERT_TRUE(web::LoadHtml(web_view_, kLinkHtml, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, kLinkHtml, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
   EXPECT_NSEQ(@{kContextMenuElementRequestId : kRequestId}, result);
@@ -586,7 +589,7 @@
        "</body>";
 
   NSString* html = [NSString stringWithFormat:kLinkHtml, kLinkDest];
-  ASSERT_TRUE(web::LoadHtml(web_view_, html, GetTestURL()));
+  ASSERT_TRUE(web::test::LoadHtml(web_view_, html, GetTestURL()));
 
   id result = FindElementAtPoint(1, 1);
   NSDictionary* expected_result = @{
diff --git a/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm b/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
index a1765c2c..2b96e44 100644
--- a/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
+++ b/ios/web/web_state/js/crw_js_window_id_manager_unittest.mm
@@ -29,25 +29,25 @@
 TEST_F(JSWindowIDManagerTest, WindowIDDifferentManager) {
   // Inject the first manager.
   WKWebView* web_view = [[WKWebView alloc] init];
-  ExecuteJavaScript(web_view,
-                    GetDocumentStartScriptForAllFrames(&browser_state_));
+  test::ExecuteJavaScript(web_view,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
 
   CRWJSWindowIDManager* manager =
       [[CRWJSWindowIDManager alloc] initWithWebView:web_view];
   [manager inject];
   EXPECT_NSEQ([manager windowID],
-              ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
 
   // Inject the second manager.
   WKWebView* web_view2 = [[WKWebView alloc] init];
-  ExecuteJavaScript(web_view2,
-                    GetDocumentStartScriptForAllFrames(&browser_state_));
+  test::ExecuteJavaScript(web_view2,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
 
   CRWJSWindowIDManager* manager2 =
       [[CRWJSWindowIDManager alloc] initWithWebView:web_view2];
   [manager2 inject];
   EXPECT_NSEQ([manager2 windowID],
-              ExecuteJavaScript(web_view2, @"window.__gCrWeb.windowId"));
+              test::ExecuteJavaScript(web_view2, @"window.__gCrWeb.windowId"));
 
   // Window IDs must be different.
   EXPECT_NSNE([manager windowID], [manager2 windowID]);
@@ -56,8 +56,8 @@
 // Tests that injecting multiple times creates a new window ID.
 TEST_F(JSWindowIDManagerTest, MultipleInjections) {
   WKWebView* web_view = [[WKWebView alloc] init];
-  ExecuteJavaScript(web_view,
-                    GetDocumentStartScriptForAllFrames(&browser_state_));
+  test::ExecuteJavaScript(web_view,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
 
   // First injection.
   CRWJSWindowIDManager* manager =
@@ -65,12 +65,12 @@
   [manager inject];
   NSString* windowID = [manager windowID];
   EXPECT_NSEQ(windowID,
-              ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
 
   // Second injection.
   [manager inject];
   EXPECT_NSEQ([manager windowID],
-              ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
 
   EXPECT_NSNE(windowID, [manager windowID]);
 }
@@ -83,13 +83,13 @@
       [[CRWJSWindowIDManager alloc] initWithWebView:web_view];
   [manager inject];
   EXPECT_TRUE([manager windowID]);
-  EXPECT_FALSE(ExecuteJavaScript(web_view, @"window.__gCrWeb"));
+  EXPECT_FALSE(test::ExecuteJavaScript(web_view, @"window.__gCrWeb"));
 
   // Now inject window.__gCrWeb and check if window ID injection retried.
-  ExecuteJavaScript(web_view,
-                    GetDocumentStartScriptForAllFrames(&browser_state_));
+  test::ExecuteJavaScript(web_view,
+                          GetDocumentStartScriptForAllFrames(&browser_state_));
   EXPECT_NSEQ([manager windowID],
-              ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
+              test::ExecuteJavaScript(web_view, @"window.__gCrWeb.windowId"));
 }
 
 }  // namespace web
diff --git a/ios/web/web_state/js/page_script_util_unittest.mm b/ios/web/web_state/js/page_script_util_unittest.mm
index 65ddb1a..8f69a98 100644
--- a/ios/web/web_state/js/page_script_util_unittest.mm
+++ b/ios/web/web_state/js/page_script_util_unittest.mm
@@ -38,20 +38,21 @@
 // __gCrWeb object.
 TEST_F(PageScriptUtilTest, WKWebViewEarlyPageScript) {
   WKWebView* web_view = BuildWKWebView(CGRectZero, GetBrowserState());
-  ExecuteJavaScript(web_view,
-                    GetDocumentStartScriptForAllFrames(GetBrowserState()));
-  EXPECT_NSEQ(@"object", ExecuteJavaScript(web_view, @"typeof __gCrWeb"));
+  test::ExecuteJavaScript(
+      web_view, GetDocumentStartScriptForAllFrames(GetBrowserState()));
+  EXPECT_NSEQ(@"object", test::ExecuteJavaScript(web_view, @"typeof __gCrWeb"));
 }
 
 // Tests that embedder's WKWebView script is included into early script.
 TEST_F(PageScriptUtilTest, WKEmbedderScript) {
   GetWebClient()->SetEarlyPageScript(@"__gCrEmbedder = {};");
   WKWebView* web_view = BuildWKWebView(CGRectZero, GetBrowserState());
-  ExecuteJavaScript(web_view,
-                    GetDocumentStartScriptForAllFrames(GetBrowserState()));
-  ExecuteJavaScript(web_view,
-                    GetDocumentStartScriptForMainFrame(GetBrowserState()));
-  EXPECT_NSEQ(@"object", ExecuteJavaScript(web_view, @"typeof __gCrEmbedder"));
+  test::ExecuteJavaScript(
+      web_view, GetDocumentStartScriptForAllFrames(GetBrowserState()));
+  test::ExecuteJavaScript(
+      web_view, GetDocumentStartScriptForMainFrame(GetBrowserState()));
+  EXPECT_NSEQ(@"object",
+              test::ExecuteJavaScript(web_view, @"typeof __gCrEmbedder"));
 }
 
 }  // namespace
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index c7092aa..817dd30a 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -111,6 +111,7 @@
     "//base",
     "//ios/web/public",
     "//ios/web/web_state/js",
+    "//ios/web/web_state/js:script_util",
   ]
 
   sources = [
diff --git a/ios/web_view/internal/web_view_web_client_unittest.mm b/ios/web_view/internal/web_view_web_client_unittest.mm
index 0692f344..da231ea 100644
--- a/ios/web_view/internal/web_view_web_client_unittest.mm
+++ b/ios/web_view/internal/web_view_web_client_unittest.mm
@@ -31,14 +31,14 @@
 TEST_F(WebViewWebClientTest, WKWebViewEarlyPageScriptAutofillController) {
   // WebView scripts rely on __gCrWeb object presence.
   WKWebView* web_view = web::BuildWKWebView(CGRectZero, GetBrowserState());
-  web::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
+  web::test::ExecuteJavaScript(web_view, @"__gCrWeb = {};");
 
   web::ScopedTestingWebClient web_client(std::make_unique<WebViewWebClient>());
   NSString* script =
       web_client.Get()->GetDocumentStartScriptForMainFrame(GetBrowserState());
-  web::ExecuteJavaScript(web_view, script);
-  EXPECT_NSEQ(@"object",
-              web::ExecuteJavaScript(web_view, @"typeof __gCrWeb.autofill"));
+  web::test::ExecuteJavaScript(web_view, script);
+  EXPECT_NSEQ(@"object", web::test::ExecuteJavaScript(
+                             web_view, @"typeof __gCrWeb.autofill"));
 }
 
 }  // namespace ios_web_view
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc
index c062698..3b5ed32 100644
--- a/media/audio/audio_input_controller.cc
+++ b/media/audio/audio_input_controller.cc
@@ -552,7 +552,6 @@
 
   UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS);
 
-  UMA_HISTOGRAM_PERCENTAGE("Media.MicrophoneVolume", microphone_volume_percent);
   log_string = base::StringPrintf(
       "AIC::OnData: microphone volume=%d%%", microphone_volume_percent);
   if (microphone_volume_percent < kLowLevelMicrophoneLevelPercent)
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc
index 53586b88..46d432c 100644
--- a/media/audio/mac/audio_low_latency_input_mac.cc
+++ b/media/audio/mac/audio_low_latency_input_mac.cc
@@ -145,25 +145,15 @@
 // Adds extra system information to Media.AudioXXXMac UMA statistics.
 // Only called when it has been detected that audio callbacks does not start
 // as expected.
-static void AddSystemInfoToUMA(bool is_on_battery, int num_resumes) {
-  // Logs true or false depending on if the machine is on battery power or not.
-  UMA_HISTOGRAM_BOOLEAN("Media.Audio.IsOnBatteryPowerMac", is_on_battery);
+static void AddSystemInfoToUMA() {
   // Number of logical processors/cores on the current machine.
   UMA_HISTOGRAM_COUNTS("Media.Audio.LogicalProcessorsMac",
                        base::SysInfo::NumberOfProcessors());
   // Number of physical processors/cores on the current machine.
   UMA_HISTOGRAM_COUNTS("Media.Audio.PhysicalProcessorsMac",
                        NumberOfPhysicalProcessors());
-  // Counts number of times the system has resumed from power suspension.
-  UMA_HISTOGRAM_COUNTS_1000("Media.Audio.ResumeEventsMac", num_resumes);
-  // System uptime in hours.
-  UMA_HISTOGRAM_COUNTS_1000("Media.Audio.UptimeMac",
-                            base::SysInfo::Uptime().InHours());
-  DVLOG(1) << "uptime: " << base::SysInfo::Uptime().InHours();
   DVLOG(1) << "logical processors: " << base::SysInfo::NumberOfProcessors();
   DVLOG(1) << "physical processors: " << NumberOfPhysicalProcessors();
-  DVLOG(1) << "battery power: " << is_on_battery;
-  DVLOG(1) << "resume events: " << num_resumes;
 }
 
 // Finds the first subdevice, in an aggregate device, with output streams.
@@ -228,7 +218,6 @@
             kNumberOfBlocksBufferInFifo),
       got_input_callback_(false),
       input_callback_is_active_(false),
-      start_was_deferred_(false),
       buffer_size_was_changed_(false),
       audio_unit_render_has_worked_(false),
       noise_reduction_suppressed_(false),
@@ -686,7 +675,6 @@
   // Check if we should defer Start() for http://crbug.com/160920.
   if (manager_->ShouldDeferStreamStart()) {
     LOG(WARNING) << "Start of input audio is deferred";
-    start_was_deferred_ = true;
     // Use a cancellable closure so that if Stop() is called before Start()
     // actually runs, we can cancel the pending start.
     deferred_start_cb_.Reset(base::Bind(&AUAudioInputStream::Start,
@@ -783,21 +771,6 @@
   // Uninitialize and dispose the audio unit.
   CloseAudioUnit();
 
-  // Add more UMA stats but only if AGC was activated, i.e. for e.g. WebRTC
-  // audio input streams.
-  if (GetAutomaticGainControl()) {
-    // Log whether call to Start() was deferred or not. To be compared with
-    // Media.Audio.InputStartWasDeferredMac which logs the same value but only
-    // when input audio fails to start.
-    UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredAudioWorkedMac",
-                          start_was_deferred_);
-    // Log if a change of I/O buffer size was required. To be compared with
-    // Media.Audio.InputBufferSizeWasChangedMac which logs the same value but
-    // only when input audio fails to start.
-    UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedAudioWorkedMac",
-                          buffer_size_was_changed_);
-  }
-
   // Inform the audio manager that we have been closed. This will cause our
   // destruction.
   manager_->ReleaseInputStream(this);
@@ -1378,16 +1351,8 @@
 
 void AUAudioInputStream::AddHistogramsForFailedStartup() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac",
-                        start_was_deferred_);
   UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac",
                         buffer_size_was_changed_);
-  UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac",
-                            manager_->output_streams());
-  UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac",
-                            manager_->low_latency_input_streams());
-  UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfBasicInputStreamsMac",
-                            manager_->basic_input_streams());
   // |input_params_.frames_per_buffer()| is set at construction and corresponds
   // to the requested (by the client) number of audio frames per I/O buffer
   // connected to the selected input device. Ideally, this size will be the same
@@ -1404,14 +1369,8 @@
   base::UmaHistogramSparse("Media.Audio.ActualInputBufferFrameSizeMac",
                            io_buffer_frame_size_);
   DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_;
-  // TODO(henrika): this value will currently always report true. It should
-  // be fixed when we understand the problem better.
-  UMA_HISTOGRAM_BOOLEAN("Media.Audio.AutomaticGainControlMac",
-                        GetAutomaticGainControl());
-  // Add information about things like number of logical processors, number
-  // of system resume events etc.
-  AddSystemInfoToUMA(manager_->IsOnBatteryPower(),
-                     manager_->GetNumberOfResumeNotifications());
+  // Add information about things like number of logical processors etc.
+  AddSystemInfoToUMA();
 }
 
 void AUAudioInputStream::UpdateCaptureTimestamp(
diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h
index b0b6e62..edb8da6 100644
--- a/media/audio/mac/audio_low_latency_input_mac.h
+++ b/media/audio/mac/audio_low_latency_input_mac.h
@@ -236,10 +236,6 @@
   // This timer lives on the main browser thread.
   std::unique_ptr<base::OneShotTimer> input_callback_timer_;
 
-  // Set to true if the Start() call was delayed.
-  // See AudioManagerMac::ShouldDeferStreamStart() for details.
-  bool start_was_deferred_;
-
   // Set to true if the audio unit's IO buffer was changed when Open() was
   // called.
   bool buffer_size_was_changed_;
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc
index 74e74d8a..e61d2e80 100644
--- a/media/gpu/android/media_codec_video_decoder.cc
+++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -503,6 +503,10 @@
 
 void MediaCodecVideoDecoder::FlushCodec() {
   DVLOG(2) << __func__;
+
+  // If a deferred flush was pending, then it isn't anymore.
+  deferred_flush_pending_ = false;
+
   if (!codec_ || codec_->IsFlushed())
     return;
 
@@ -591,7 +595,10 @@
   // If the codec is drained, flush it when there is a pending decode and no
   // unreleased output buffers. This lets us avoid both unbacking frames when we
   // flush, and flushing unnecessarily, like at EOS.
-  if (codec_->IsDrained()) {
+  //
+  // Often, we'll elide the eos to drain the codec, but we want to pretend that
+  // we did.  In this case, we should also flush.
+  if (codec_->IsDrained() || deferred_flush_pending_) {
     if (!codec_->HasUnreleasedOutputBuffers() && !pending_decodes_.empty()) {
       FlushCodec();
       return true;
@@ -691,8 +698,9 @@
   }
 
   // If we're draining for reset or destroy we can discard |output_buffer|
-  // without rendering it.
-  if (drain_type_)
+  // without rendering it.  This is also true if we elided the drain itself,
+  // and deferred a flush that would have happened when the drain completed.
+  if (drain_type_ || deferred_flush_pending_)
     return true;
 
   // Record the frame type that we're sending and some information about why.
@@ -772,6 +780,11 @@
   // instead. Draining is responsible for a lot of complexity.
   if (decoder_config_.codec() != kCodecVP8 || !codec_ || codec_->IsFlushed() ||
       codec_->IsDrained()) {
+    // If the codec isn't already drained or flushed, then we have to remember
+    // that we owe it a flush.  We also have to remember not to deliver any
+    // output buffers that might still be in progress in the codec.
+    deferred_flush_pending_ =
+        codec_ && !codec_->IsDrained() && !codec_->IsFlushed();
     OnCodecDrained();
     return;
   }
@@ -795,7 +808,14 @@
   }
 
   std::move(reset_cb_).Run();
-  FlushCodec();
+
+  // Flush the codec unless (a) it's already flushed, (b) it's drained and the
+  // flush will be handled automatically on the next decode, or (c) we've
+  // elided the eos and want to defer the flush.
+  if (codec_ && !codec_->IsFlushed() && !codec_->IsDrained() &&
+      !deferred_flush_pending_) {
+    FlushCodec();
+  }
 }
 
 void MediaCodecVideoDecoder::EnterTerminalState(State state) {
diff --git a/media/gpu/android/media_codec_video_decoder.h b/media/gpu/android/media_codec_video_decoder.h
index 6a5c30c..fbfc05c8 100644
--- a/media/gpu/android/media_codec_video_decoder.h
+++ b/media/gpu/android/media_codec_video_decoder.h
@@ -283,6 +283,12 @@
 
   bool using_async_api_ = false;
 
+  // Should we flush the codec on the next decode, and pretend that it is
+  // drained currently?  Note that we'll automatically flush if the codec is
+  // drained; this flag indicates that we also elided the drain, so the codec is
+  // in some random state, possibly with output buffers pending.
+  bool deferred_flush_pending_ = false;
+
   // Optional crypto object from the Cdm.
   base::android::ScopedJavaGlobalRef<jobject> media_crypto_;
 
diff --git a/media/renderers/renderer_impl.cc b/media/renderers/renderer_impl.cc
index fe5bbdc..fe652014 100644
--- a/media/renderers/renderer_impl.cc
+++ b/media/renderers/renderer_impl.cc
@@ -816,11 +816,15 @@
     return;
 
   if (type == DemuxerStream::AUDIO) {
-    DCHECK(!audio_ended_);
+    // If all streams are ended, do not propagate a redundant ended event.
+    if (audio_ended_ && PlaybackHasEnded())
+      return;
     audio_ended_ = true;
   } else {
-    DCHECK(!video_ended_);
     DCHECK(video_renderer_);
+    // If all streams are ended, do not propagate a redundant ended event.
+    if (audio_ended_ && PlaybackHasEnded())
+      return;
     video_ended_ = true;
     video_renderer_->OnTimeStopped();
   }
@@ -907,7 +911,11 @@
 void RendererImpl::CleanUpTrackChange(base::RepeatingClosure on_finished,
                                       bool* ended,
                                       bool* playing) {
-  *ended = *playing = false;
+  *playing = false;
+  // If either stream is alive (i.e. hasn't reached ended state), ended can be
+  // set to false. If both streams are dead, keep ended=true.
+  if ((audio_renderer_ && !audio_ended_) || (video_renderer_ && !video_ended_))
+    *ended = false;
   std::move(on_finished).Run();
 }
 
diff --git a/mojo/public/cpp/base/BUILD.gn b/mojo/public/cpp/base/BUILD.gn
index 0be8bd0..5f0f2bcb 100644
--- a/mojo/public/cpp/base/BUILD.gn
+++ b/mojo/public/cpp/base/BUILD.gn
@@ -10,6 +10,8 @@
   sources = [
     "big_buffer.cc",
     "big_buffer.h",
+    "shared_memory_utils.cc",
+    "shared_memory_utils.h",
   ]
 
   defines = [ "IS_MOJO_BASE_IMPL" ]
diff --git a/mojo/public/cpp/base/shared_memory_utils.cc b/mojo/public/cpp/base/shared_memory_utils.cc
new file mode 100644
index 0000000..0f5f845
--- /dev/null
+++ b/mojo/public/cpp/base/shared_memory_utils.cc
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/base/shared_memory_utils.h"
+
+#include "base/memory/shared_memory_mapping.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace mojo {
+
+base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size) {
+  auto writable_region = CreateWritableSharedMemoryRegion(size);
+  if (!writable_region.IsValid())
+    return {};
+
+  base::WritableSharedMemoryMapping mapping = writable_region.Map();
+  return {base::WritableSharedMemoryRegion::ConvertToReadOnly(
+              std::move(writable_region)),
+          std::move(mapping)};
+}
+
+base::UnsafeSharedMemoryRegion CreateUnsafeSharedMemoryRegion(size_t size) {
+  auto writable_region = CreateWritableSharedMemoryRegion(size);
+  if (!writable_region.IsValid())
+    return base::UnsafeSharedMemoryRegion();
+
+  return base::WritableSharedMemoryRegion::ConvertToUnsafe(
+      std::move(writable_region));
+}
+
+base::WritableSharedMemoryRegion CreateWritableSharedMemoryRegion(size_t size) {
+  mojo::ScopedSharedBufferHandle handle =
+      mojo::SharedBufferHandle::Create(size);
+  if (!handle.is_valid())
+    return base::WritableSharedMemoryRegion();
+
+  return mojo::UnwrapWritableSharedMemoryRegion(std::move(handle));
+}
+
+}  // namespace mojo
diff --git a/mojo/public/cpp/base/shared_memory_utils.h b/mojo/public/cpp/base/shared_memory_utils.h
new file mode 100644
index 0000000..e73dba1f
--- /dev/null
+++ b/mojo/public/cpp/base/shared_memory_utils.h
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BASE_SHARED_MEMORY_UTILS_H_
+#define MOJO_PUBLIC_CPP_BASE_SHARED_MEMORY_UTILS_H_
+
+#include "base/component_export.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/memory/writable_shared_memory_region.h"
+
+namespace mojo {
+
+// These creation methods are parallel to the base::*SharedMemoryRegion::Create
+// methods. These methods should be used instead of the base:: ones to create
+// shared memory in an unprivileged context, in which case a broker in a
+// privileged process will be used to create the region.
+//
+// IsValid() should be checked on the return value of the following methods to
+// determine if the creation was successful.
+COMPONENT_EXPORT(MOJO_BASE)
+base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size);
+COMPONENT_EXPORT(MOJO_BASE)
+base::UnsafeSharedMemoryRegion CreateUnsafeSharedMemoryRegion(size_t size);
+COMPONENT_EXPORT(MOJO_BASE)
+base::WritableSharedMemoryRegion CreateWritableSharedMemoryRegion(size_t size);
+
+}  // namespace mojo
+
+#endif  // MOJO_PUBLIC_CPP_BASE_SHARED_MEMORY_UTILS_H_
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
index d72d1976..d816e6a7 100644
--- a/mojo/public/cpp/system/buffer.h
+++ b/mojo/public/cpp/system/buffer.h
@@ -55,6 +55,13 @@
   // Copying and assignment allowed.
 
   // Creates a new SharedBufferHandle. Returns an invalid handle on failure.
+  //
+  // Note for those converting legacy shared memory to the
+  // base::*SharedMemoryRegion API: if SharedBufferHandle::Create is used for
+  // your shared memory regions, the mojo::Create*SahredMemoryRegion methods in
+  // mojo/public/cpp/base/shared_memory_utils.h should be used. These know how
+  // to use a broker to create regions in unprivileged contexts in the same way
+  // as this SharedBufferHandle::Create method.
   static ScopedSharedBufferHandle Create(uint64_t num_bytes);
 
   // Clones this shared buffer handle. If |access_mode| is READ_ONLY or this is
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 326b22e..3d06331 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -364,6 +364,8 @@
     "ssl/token_binding.h",
     "third_party/quic/core/quic_error_codes.cc",
     "third_party/quic/core/quic_error_codes.h",
+    "third_party/uri_template/uri_template.cc",
+    "third_party/uri_template/uri_template.h",
   ]
   net_unfiltered_sources = []
 
@@ -5152,6 +5154,7 @@
     "third_party/spdy/core/spdy_test_utils.h",
     "third_party/spdy/platform/api/spdy_mem_slice_test.cc",
     "third_party/spdy/platform/api/spdy_string_utils_test.cc",
+    "third_party/uri_template/uri_template_test.cc",
     "tools/content_decoder_tool/content_decoder_tool.cc",
     "tools/content_decoder_tool/content_decoder_tool.h",
     "tools/content_decoder_tool/content_decoder_tool_unittest.cc",
@@ -6203,3 +6206,15 @@
     "//net/data/ssl/certificates:generate_fuzzer_cert_includes",
   ]
 }
+
+fuzzer_test("net_uri_template_fuzzer") {
+  sources = [
+    "third_party/uri_template/uri_template_fuzzer.cc",
+  ]
+  deps = [
+    ":net_fuzzer_test_support",
+    "//base",
+    "//net",
+  ]
+  dict = "data/fuzzer_dictionaries/net_uri_template_fuzzer.dict"
+}
diff --git a/net/base/escape.cc b/net/base/escape.cc
index e4e309e54..39c0eae 100644
--- a/net/base/escape.cc
+++ b/net/base/escape.cc
@@ -357,6 +357,12 @@
   return result;
 }
 
+// Everything except alphanumerics and -._~
+// See RFC 3986 for the list of unreserved characters.
+static const Charmap kUnreservedCharmap = {
+    {0xffffffffL, 0xfc009fffL, 0x78000001L, 0xb8000001L, 0xffffffffL,
+     0xffffffffL, 0xffffffffL, 0xffffffffL}};
+
 // Everything except alphanumerics and !'()*-._~
 // See RFC 2396 for the list of reserved characters.
 static const Charmap kQueryCharmap = {{
@@ -399,6 +405,10 @@
 
 }  // namespace
 
+std::string EscapeAllExceptUnreserved(base::StringPiece text) {
+  return Escape(text, kUnreservedCharmap, false);
+}
+
 std::string EscapeQueryParamValue(base::StringPiece text, bool use_plus) {
   return Escape(text, kQueryCharmap, use_plus);
 }
diff --git a/net/base/escape.h b/net/base/escape.h
index 081b02c..0dde0c4a 100644
--- a/net/base/escape.h
+++ b/net/base/escape.h
@@ -19,6 +19,10 @@
 
 // Escaping --------------------------------------------------------------------
 
+// Escapes all characters except unreserved characters. Unreserved characters,
+// as defined in RFC 3986, include alphanumerics and -._~
+NET_EXPORT std::string EscapeAllExceptUnreserved(base::StringPiece text);
+
 // Escapes characters in text suitable for use as a query parameter value.
 // We %XX everything except alphanumerics and -_.!~*'()
 // Spaces change to "+" unless you pass usePlus=false.
diff --git a/net/cert/caching_cert_verifier_unittest.cc b/net/cert/caching_cert_verifier_unittest.cc
index 9356359..4ad7e8d 100644
--- a/net/cert/caching_cert_verifier_unittest.cc
+++ b/net/cert/caching_cert_verifier_unittest.cc
@@ -52,17 +52,17 @@
 
   error = callback.GetResult(verifier_.Verify(
       CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  std::string(), CertificateList()),
+                                  std::string()),
       &verify_result, callback.callback(), &request, NetLogWithSource()));
   ASSERT_TRUE(IsCertificateError(error));
   ASSERT_EQ(1u, verifier_.requests());
   ASSERT_EQ(0u, verifier_.cache_hits());
   ASSERT_EQ(1u, verifier_.GetCacheSize());
 
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  std::string(), CertificateList()),
-      &verify_result, callback.callback(), &request, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result, callback.callback(), &request,
+                           NetLogWithSource());
   // Synchronous completion.
   ASSERT_NE(ERR_IO_PENDING, error);
   ASSERT_TRUE(IsCertificateError(error));
@@ -111,7 +111,7 @@
 
   error = callback.GetResult(verifier_.Verify(
       CertVerifier::RequestParams(cert_chain1, "www.example.com", 0,
-                                  std::string(), CertificateList()),
+                                  std::string()),
       &verify_result, callback.callback(), &request, NetLogWithSource()));
   ASSERT_TRUE(IsCertificateError(error));
   ASSERT_EQ(1u, verifier_.requests());
@@ -120,7 +120,7 @@
 
   error = callback.GetResult(verifier_.Verify(
       CertVerifier::RequestParams(cert_chain2, "www.example.com", 0,
-                                  std::string(), CertificateList()),
+                                  std::string()),
       &verify_result, callback.callback(), &request, NetLogWithSource()));
   ASSERT_TRUE(IsCertificateError(error));
   ASSERT_EQ(2u, verifier_.requests());
diff --git a/net/cert/cert_verifier.cc b/net/cert/cert_verifier.cc
index f231dca0..444b82d 100644
--- a/net/cert/cert_verifier.cc
+++ b/net/cert/cert_verifier.cc
@@ -33,13 +33,11 @@
     scoped_refptr<X509Certificate> certificate,
     const std::string& hostname,
     int flags,
-    const std::string& ocsp_response,
-    CertificateList additional_trust_anchors)
+    const std::string& ocsp_response)
     : certificate_(std::move(certificate)),
       hostname_(hostname),
       flags_(flags),
-      ocsp_response_(ocsp_response),
-      additional_trust_anchors_(std::move(additional_trust_anchors)) {
+      ocsp_response_(ocsp_response) {
   // For efficiency sake, rather than compare all of the fields for each
   // comparison, compute a hash of their values. This is done directly in
   // this class, rather than as an overloaded hash operator, for efficiency's
@@ -55,10 +53,6 @@
   SHA256_Update(&ctx, hostname_.data(), hostname.size());
   SHA256_Update(&ctx, &flags, sizeof(flags));
   SHA256_Update(&ctx, ocsp_response.data(), ocsp_response.size());
-  for (const auto& trust_anchor : additional_trust_anchors_) {
-    SHA256_Update(&ctx, CRYPTO_BUFFER_data(trust_anchor->cert_buffer()),
-                  CRYPTO_BUFFER_len(trust_anchor->cert_buffer()));
-  }
   SHA256_Final(reinterpret_cast<uint8_t*>(
                    base::WriteInto(&key_, SHA256_DIGEST_LENGTH + 1)),
                &ctx);
@@ -91,15 +85,16 @@
 
 bool operator==(const CertVerifier::Config& lhs,
                 const CertVerifier::Config& rhs) {
-  return std::tie(lhs.enable_rev_checking,
-                  lhs.require_rev_checking_local_anchors,
-                  lhs.enable_sha1_local_anchors,
-                  lhs.disable_symantec_enforcement, lhs.crl_set) ==
-         std::tie(rhs.enable_rev_checking,
-                  rhs.require_rev_checking_local_anchors,
-                  rhs.enable_sha1_local_anchors,
-                  rhs.disable_symantec_enforcement, rhs.crl_set);
+  return std::tie(
+             lhs.enable_rev_checking, lhs.require_rev_checking_local_anchors,
+             lhs.enable_sha1_local_anchors, lhs.disable_symantec_enforcement,
+             lhs.crl_set, lhs.additional_trust_anchors) ==
+         std::tie(
+             rhs.enable_rev_checking, rhs.require_rev_checking_local_anchors,
+             rhs.enable_sha1_local_anchors, rhs.disable_symantec_enforcement,
+             rhs.crl_set, rhs.additional_trust_anchors);
 }
+
 bool operator!=(const CertVerifier::Config& lhs,
                 const CertVerifier::Config& rhs) {
   return !(lhs == rhs);
diff --git a/net/cert/cert_verifier.h b/net/cert/cert_verifier.h
index d2b65aaa..7a637bf1 100644
--- a/net/cert/cert_verifier.h
+++ b/net/cert/cert_verifier.h
@@ -58,6 +58,12 @@
     // additional certificates to be blacklisted beyond the internal blacklist,
     // whether leaves or intermediates.
     scoped_refptr<CRLSet> crl_set;
+
+    // Additional trust anchors to consider during path validation. Ordinarily,
+    // implementations of CertVerifier use trust anchors from the configured
+    // system store. This is implementation-specific plumbing for passing
+    // additional anchors through.
+    CertificateList additional_trust_anchors;
   };
 
   class Request {
@@ -100,17 +106,12 @@
   // |ocsp_response| is optional, but if non-empty, should contain an OCSP
   // response obtained via OCSP stapling. It may be ignored by the
   // CertVerifier.
-  //
-  // |additional_trust_anchors| is optional, but if non-empty, should contain
-  // additional certificates to be treated as trust anchors. It may be ignored
-  // by the CertVerifier.
   class NET_EXPORT RequestParams {
    public:
     RequestParams(scoped_refptr<X509Certificate> certificate,
                   const std::string& hostname,
                   int flags,
-                  const std::string& ocsp_response,
-                  CertificateList additional_trust_anchors);
+                  const std::string& ocsp_response);
     RequestParams(const RequestParams& other);
     ~RequestParams();
 
@@ -120,9 +121,6 @@
     const std::string& hostname() const { return hostname_; }
     int flags() const { return flags_; }
     const std::string& ocsp_response() const { return ocsp_response_; }
-    const CertificateList& additional_trust_anchors() const {
-      return additional_trust_anchors_;
-    }
 
     bool operator==(const RequestParams& other) const;
     bool operator<(const RequestParams& other) const;
@@ -132,7 +130,6 @@
     std::string hostname_;
     int flags_;
     std::string ocsp_response_;
-    CertificateList additional_trust_anchors_;
 
     // Used to optimize sorting/indexing comparisons.
     std::string key_;
diff --git a/net/cert/cert_verifier_unittest.cc b/net/cert/cert_verifier_unittest.cc
index cccb12d..6bc31314 100644
--- a/net/cert/cert_verifier_unittest.cc
+++ b/net/cert/cert_verifier_unittest.cc
@@ -36,10 +36,6 @@
                                         std::move(chain));
   ASSERT_TRUE(combined_cert.get());
 
-  const CertificateList empty_list;
-  CertificateList test_list;
-  test_list.push_back(ok_cert);
-
   struct {
     // Keys to test
     CertVerifier::RequestParams key1;
@@ -51,36 +47,36 @@
       {
           // Test for basic equivalence.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           true,
       },
       {
           // Test that different certificates but with the same CA and for
           // the same host are different validation keys.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           CertVerifier::RequestParams(expired_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           false,
       },
       {
           // Test that the same EE certificate for the same host, but with
           // different chains are different validation keys.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           CertVerifier::RequestParams(combined_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           false,
       },
       {
           // The same certificate, with the same chain, but for different
           // hosts are different validation keys.
           CertVerifier::RequestParams(ok_cert, "www1.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           CertVerifier::RequestParams(ok_cert, "www2.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           false,
       },
       {
@@ -88,26 +84,17 @@
           // are different validation keys.
           CertVerifier::RequestParams(
               ok_cert, "www.example.test",
-              CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES, std::string(),
-              empty_list),
+              CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES, std::string()),
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
-          false,
-      },
-      {
-          // Different additional_trust_anchors.
-          CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
-          CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), test_list),
+                                      std::string()),
           false,
       },
       {
           // Different OCSP responses.
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      "ocsp response", empty_list),
+                                      "ocsp response"),
           CertVerifier::RequestParams(ok_cert, "www.example.test", 0,
-                                      std::string(), empty_list),
+                                      std::string()),
           false,
       },
   };
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index 7e9b0aa..eb082fae 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -253,7 +253,7 @@
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(&DoVerifyOnWorkerThread, verify_proc, key_.certificate(),
                        key_.hostname(), key_.ocsp_response(), flags,
-                       config.crl_set, key_.additional_trust_anchors()),
+                       config.crl_set, config.additional_trust_anchors),
         base::BindOnce(&CertVerifierJob::OnJobCompleted,
                        weak_ptr_factory_.GetWeakPtr(), config_id));
   }
diff --git a/net/cert/multi_threaded_cert_verifier_unittest.cc b/net/cert/multi_threaded_cert_verifier_unittest.cc
index ecba4089..29c383f 100644
--- a/net/cert/multi_threaded_cert_verifier_unittest.cc
+++ b/net/cert/multi_threaded_cert_verifier_unittest.cc
@@ -104,16 +104,16 @@
   TestCompletionCallback callback2;
   std::unique_ptr<CertVerifier::Request> request2;
 
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  std::string(), CertificateList()),
-      &verify_result, callback.callback(), &request, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result, callback.callback(), &request,
+                           NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request);
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  std::string(), CertificateList()),
-      &verify_result2, callback2.callback(), &request2, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result2, callback2.callback(), &request2,
+                           NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request2);
   error = callback.WaitForResult();
@@ -135,10 +135,10 @@
   CertVerifyResult verify_result;
   std::unique_ptr<CertVerifier::Request> request;
 
-  error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                  std::string(), CertificateList()),
-      &verify_result, base::BindOnce(&FailTest), &request, NetLogWithSource());
+  error = verifier_.Verify(CertVerifier::RequestParams(
+                               test_cert, "www.example.com", 0, std::string()),
+                           &verify_result, base::BindOnce(&FailTest), &request,
+                           NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
   ASSERT_TRUE(request);
   request.reset();
@@ -150,7 +150,7 @@
   for (int i = 0; i < 5; ++i) {
     error = verifier_.Verify(
         CertVerifier::RequestParams(test_cert, "www2.example.com", 0,
-                                    std::string(), CertificateList()),
+                                    std::string()),
         &verify_result, callback.callback(), &request, NetLogWithSource());
     ASSERT_THAT(error, IsError(ERR_IO_PENDING));
     EXPECT_TRUE(request);
@@ -180,7 +180,7 @@
     ANNOTATE_SCOPED_MEMORY_LEAK;
     error = verifier_.Verify(
         CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                    std::string(), CertificateList()),
+                                    std::string()),
         &verify_result, callback.callback(), &request, NetLogWithSource());
   }
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
@@ -220,37 +220,32 @@
 
   // Start 3 unique requests.
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0, std::string(),
-                                  CertificateList()),
+      CertVerifier::RequestParams(test_cert, domain2, 0, std::string()),
       &verify_result1, callback1.callback(), &request1, NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request1);
 
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0, std::string(),
-                                  CertificateList()),
+      CertVerifier::RequestParams(test_cert, domain2, 0, std::string()),
       &verify_result2, callback2.callback(), &request2, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request2);
 
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain3, 0, std::string(),
-                                  CertificateList()),
+      CertVerifier::RequestParams(test_cert, domain3, 0, std::string()),
       &verify_result3, callback3.callback(), &request3, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request3);
 
   // Start duplicate requests (which should join to existing jobs).
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain1, 0, std::string(),
-                                  CertificateList()),
+      CertVerifier::RequestParams(test_cert, domain1, 0, std::string()),
       &verify_result4, callback4.callback(), &request4, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request4);
 
   error = verifier_.Verify(
-      CertVerifier::RequestParams(test_cert, domain2, 0, std::string(),
-                                  CertificateList()),
+      CertVerifier::RequestParams(test_cert, domain2, 0, std::string()),
       &verify_result5, callback5.callback(), &request5, NetLogWithSource());
   EXPECT_THAT(error, IsError(ERR_IO_PENDING));
   EXPECT_TRUE(request5);
@@ -303,7 +298,7 @@
     std::unique_ptr<CertVerifier::Request> request;
     int error = verifier_.Verify(
         CertVerifier::RequestParams(test_cert, "www.example.com", 0,
-                                    std::string(), CertificateList()),
+                                    std::string()),
         &verify_result, callback.callback(), &request, NetLogWithSource());
     ASSERT_THAT(error, IsError(ERR_IO_PENDING));
     EXPECT_TRUE(request);
diff --git a/net/cert_net/nss_ocsp_unittest.cc b/net/cert_net/nss_ocsp_unittest.cc
index 9843d9c..9c42f2f 100644
--- a/net/cert_net/nss_ocsp_unittest.cc
+++ b/net/cert_net/nss_ocsp_unittest.cc
@@ -150,7 +150,7 @@
   int flags = 0;
   int error = verifier()->Verify(
       CertVerifier::RequestParams(test_cert, "aia-host.invalid", flags,
-                                  std::string(), CertificateList()),
+                                  std::string()),
       &verify_result, test_callback.callback(), &request, NetLogWithSource());
   ASSERT_THAT(error, IsError(ERR_IO_PENDING));
 
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index d4382d39..40f30163 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -921,8 +921,9 @@
         GetKey(cookie_ptr->Domain()), std::move(cookie), false);
     const Time cookie_access_time(cookie_ptr->LastAccessDate());
     if (earliest_access_time_.is_null() ||
-        cookie_access_time < earliest_access_time_)
+        cookie_access_time < earliest_access_time_) {
       earliest_access_time_ = cookie_access_time;
+    }
 
     if (ContainsControlCharacter(cookie_ptr->Name()) ||
         ContainsControlCharacter(cookie_ptr->Value())) {
@@ -1210,8 +1211,9 @@
                     base::BindRepeating(&NetLogCookieMonsterCookieAdded,
                                         cc.get(), sync_to_store));
   if ((cc_ptr->IsPersistent() || persist_session_cookies_) && store_.get() &&
-      sync_to_store)
+      sync_to_store) {
     store_->AddCookie(*cc_ptr);
+  }
   CookieMap::iterator inserted =
       cookies_.insert(CookieMap::value_type(key, std::move(cc)));
 
@@ -1392,8 +1394,9 @@
   }
 
   if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() &&
-      sync_to_store)
+      sync_to_store) {
     store_->DeleteCookie(*cc);
+  }
   change_dispatcher_.DispatchChange(*cc, mapping.cause, mapping.notify);
   cookies_.erase(it);
 }
diff --git a/net/data/fuzzer_dictionaries/net_uri_template_fuzzer.dict b/net/data/fuzzer_dictionaries/net_uri_template_fuzzer.dict
new file mode 100644
index 0000000..55a3357
--- /dev/null
+++ b/net/data/fuzzer_dictionaries/net_uri_template_fuzzer.dict
@@ -0,0 +1,32 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Fuzzer dictionary for URI template parser
+
+# Variable expressions
+"{foo}"
+"{+foo}"
+"{#foo}"
+"{.foo}"
+"{/foo}"
+"{;foo}"
+"{?foo}"
+"{&foo}"
+"{foo,bar}"
+"{?foo,bar,baz}"
+"?{foo,bar,baz}"
+
+# Variable names
+"foo"
+"bar"
+"baz"
+
+# Variable values
+" "
+"%"
+"%31"
+"\x80"
+
+# Magic end of string marker used by FuzzedDataProvider
+"\\a"
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index ea74e07..ce51f2c 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -198,22 +198,26 @@
                                 int buf_len,
                                 CompletionOnceCallback callback) {
   DCHECK(user_callback_.is_null());
-  if (next_state_ != STATE_DONE) {
-    // We're trying to read the body of the response but we're still trying
-    // to establish an SSL tunnel through the proxy.  We can't read these
-    // bytes when establishing a tunnel because they might be controlled by
-    // an active network attacker.  We don't worry about this for HTTP
-    // because an active network attacker can already control HTTP sessions.
-    // We reach this case when the user cancels a 407 proxy auth prompt.
-    // See http://crbug.com/8473.
-    DCHECK_EQ(407, response_.headers->response_code());
-
+  if (!CheckDone())
     return ERR_TUNNEL_CONNECTION_FAILED;
-  }
 
   return transport_->socket()->Read(buf, buf_len, std::move(callback));
 }
 
+int HttpProxyClientSocket::ReadIfReady(IOBuffer* buf,
+                                       int buf_len,
+                                       CompletionOnceCallback callback) {
+  DCHECK(user_callback_.is_null());
+  if (!CheckDone())
+    return ERR_TUNNEL_CONNECTION_FAILED;
+
+  return transport_->socket()->ReadIfReady(buf, buf_len, std::move(callback));
+}
+
+int HttpProxyClientSocket::CancelReadIfReady() {
+  return transport_->socket()->CancelReadIfReady();
+}
+
 int HttpProxyClientSocket::Write(
     IOBuffer* buf,
     int buf_len,
@@ -497,6 +501,22 @@
   return DidDrainBodyForAuthRestart();
 }
 
+bool HttpProxyClientSocket::CheckDone() {
+  if (next_state_ != STATE_DONE) {
+    // We're trying to read the body of the response but we're still trying
+    // to establish an SSL tunnel through the proxy.  We can't read these
+    // bytes when establishing a tunnel because they might be controlled by
+    // an active network attacker.  We don't worry about this for HTTP
+    // because an active network attacker can already control HTTP sessions.
+    // We reach this case when the user cancels a 407 proxy auth prompt.
+    // See http://crbug.com/8473.
+    DCHECK_EQ(407, response_.headers->response_code());
+
+    return false;
+  }
+  return true;
+}
+
 //----------------------------------------------------------------
 
 }  // namespace net
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index f9e9f65a..38c65bf8 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -81,6 +81,10 @@
   int Read(IOBuffer* buf,
            int buf_len,
            CompletionOnceCallback callback) override;
+  int ReadIfReady(IOBuffer* buf,
+                  int buf_len,
+                  CompletionOnceCallback callback) override;
+  int CancelReadIfReady() override;
   int Write(IOBuffer* buf,
             int buf_len,
             CompletionOnceCallback callback,
@@ -125,6 +129,9 @@
   int DoDrainBody();
   int DoDrainBodyComplete(int result);
 
+  // Returns whether |next_state_| is STATE_DONE.
+  bool CheckDone();
+
   CompletionRepeatingCallback io_callback_;
   State next_state_;
 
diff --git a/net/http/http_proxy_client_socket_wrapper.cc b/net/http/http_proxy_client_socket_wrapper.cc
index 90aad07..1676547a 100644
--- a/net/http/http_proxy_client_socket_wrapper.cc
+++ b/net/http/http_proxy_client_socket_wrapper.cc
@@ -305,6 +305,20 @@
   return ERR_SOCKET_NOT_CONNECTED;
 }
 
+int HttpProxyClientSocketWrapper::ReadIfReady(IOBuffer* buf,
+                                              int buf_len,
+                                              CompletionOnceCallback callback) {
+  if (transport_socket_)
+    return transport_socket_->ReadIfReady(buf, buf_len, std::move(callback));
+  return ERR_SOCKET_NOT_CONNECTED;
+}
+
+int HttpProxyClientSocketWrapper::CancelReadIfReady() {
+  if (transport_socket_)
+    return transport_socket_->CancelReadIfReady();
+  return OK;
+}
+
 int HttpProxyClientSocketWrapper::Write(
     IOBuffer* buf,
     int buf_len,
diff --git a/net/http/http_proxy_client_socket_wrapper.h b/net/http/http_proxy_client_socket_wrapper.h
index 0e7f11ec..ab8a1e7a 100644
--- a/net/http/http_proxy_client_socket_wrapper.h
+++ b/net/http/http_proxy_client_socket_wrapper.h
@@ -115,6 +115,10 @@
   int Read(IOBuffer* buf,
            int buf_len,
            CompletionOnceCallback callback) override;
+  int ReadIfReady(IOBuffer* buf,
+                  int buf_len,
+                  CompletionOnceCallback callback) override;
+  int CancelReadIfReady() override;
   int Write(IOBuffer* buf,
             int buf_len,
             CompletionOnceCallback callback,
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index b3ae27543..63029fb 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -3165,3 +3165,13 @@
 //    "type": <Classname of persistent cookie store>
 //  }
 EVENT_TYPE(COOKIE_PERSISTENT_STORE_CLOSED)
+
+// Event emitted when getting cookies is blocked by a NetworkDelegate.
+//  {
+//  }
+EVENT_TYPE(COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE)
+
+// Event emitted when setting cookies is blocked by a NetworkDelegate.
+//  {
+//  }
+EVENT_TYPE(COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE)
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
index 91be2d9..f3c9753 100644
--- a/net/quic/crypto/proof_verifier_chromium.cc
+++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -369,7 +369,7 @@
 
   return verifier_->Verify(
       CertVerifier::RequestParams(cert_, hostname_, cert_verify_flags_,
-                                  std::string(), CertificateList()),
+                                  std::string()),
       &verify_details_->cert_verify_result,
       base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
                  base::Unretained(this)),
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 1925d8a..aafeab8 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -1186,7 +1186,7 @@
   return cert_verifier_->Verify(
       CertVerifier::RequestParams(server_cert_, host_and_port_.host(),
                                   ssl_config_.GetCertVerifyFlags(),
-                                  ocsp_response.as_string(), CertificateList()),
+                                  ocsp_response.as_string()),
       &server_cert_verify_result_,
       base::Bind(&SSLClientSocketImpl::OnHandshakeIOComplete,
                  base::Unretained(this)),
diff --git a/net/third_party/uri_template/LICENSE b/net/third_party/uri_template/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/net/third_party/uri_template/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/net/third_party/uri_template/OWNERS b/net/third_party/uri_template/OWNERS
new file mode 100644
index 0000000..3c466ff
--- /dev/null
+++ b/net/third_party/uri_template/OWNERS
@@ -0,0 +1,3 @@
+mmenke@chromium.org
+
+# COMPONENT: Internals>Network>DNS
diff --git a/net/third_party/uri_template/README.chromium b/net/third_party/uri_template/README.chromium
new file mode 100644
index 0000000..e0565d6
--- /dev/null
+++ b/net/third_party/uri_template/README.chromium
@@ -0,0 +1,22 @@
+Name: URI Template Parser
+Short Name: uri_template
+URL: https://github.com/google/google-api-cpp-client/
+Revision: d0bbe169d81a50936ec5fcea4e6dbcfb97303f13
+Date: August 6, 2018
+License: Apache 2.0
+License File: LICENSE
+
+Description:
+This code is derived from the URI template parsing code in the Google API C++
+Client.
+
+Local Modifications:
+There are significant adaptations:
+- Improved conformance to RFC6570 for Level 3 templates (most notably, multiple
+variables per expression are now supported).
+- Removed Level 4 template parsing capabilities.
+- Replaced callback mechanism for populating variables with a map.
+- Altered meaning and type of return value for Expand method.
+- Added detection of malformed URI templates.
+- Removed some minor dependencies through refactoring.
+- Significant changes to tests.
diff --git a/net/third_party/uri_template/uri_template.cc b/net/third_party/uri_template/uri_template.cc
new file mode 100644
index 0000000..a8c72cb
--- /dev/null
+++ b/net/third_party/uri_template/uri_template.cc
@@ -0,0 +1,198 @@
+/*
+ * \copyright Copyright 2013 Google Inc. All Rights Reserved.
+ * \license @{
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @}
+ */
+
+// Implementation of RFC 6570 based on (open source implementation) at
+//   java/com/google/api/client/http/UriTemplate.java
+// The URI Template spec is at http://tools.ietf.org/html/rfc6570
+// Templates up to level 3 are supported.
+
+#include "net/third_party/uri_template/uri_template.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/strings/string_split.h"
+#include "net/base/escape.h"
+
+using std::string;
+
+namespace uri_template {
+
+namespace {
+
+// The UriTemplateConfig is used to represent variable sections and to construct
+// the expanded url.
+struct UriTemplateConfig {
+ public:
+  UriTemplateConfig(const char* prefix,
+                    const char* joiner,
+                    bool requires_variable_assignment,
+                    bool allow_reserved_expansion,
+                    bool no_variable_assignment_if_empty = false)
+      : prefix_(prefix),
+        joiner_(joiner),
+        requires_variable_assignment_(requires_variable_assignment),
+        no_variable_assignment_if_empty_(no_variable_assignment_if_empty),
+        allow_reserved_expansion_(allow_reserved_expansion) {}
+
+  void AppendValue(const string& variable,
+                   const string& value,
+                   bool use_prefix,
+                   string* target) const {
+    string joiner = use_prefix ? prefix_ : joiner_;
+    if (requires_variable_assignment_) {
+      if (value.empty() && no_variable_assignment_if_empty_) {
+        target->append(joiner + EscapedValue(variable));
+      } else {
+        target->append(joiner + EscapedValue(variable) + "=" +
+                       EscapedValue(value));
+      }
+    } else {
+      target->append(joiner + EscapedValue(value));
+    }
+  }
+
+ private:
+  string EscapedValue(const string& value) const {
+    string escaped;
+    if (allow_reserved_expansion_) {
+      // Reserved expansion passes through reserved and pct-encoded characters.
+      escaped = net::EscapeExternalHandlerValue(value);
+    } else {
+      escaped = net::EscapeAllExceptUnreserved(value);
+    }
+    return escaped;
+  }
+
+  const char* prefix_;
+  const char* joiner_;
+  bool requires_variable_assignment_;
+  bool no_variable_assignment_if_empty_;
+  bool allow_reserved_expansion_;
+};
+
+// variable is an in-out argument. On input it is the content between the
+// '{}' in the source. On result the control parameters are stripped off
+// leaving just the comma-separated variable name(s) that we should try to
+// resolve.
+UriTemplateConfig MakeConfig(string* variable) {
+  switch (*variable->data()) {
+    // Reserved expansion.
+    case '+':
+      *variable = variable->substr(1);
+      return UriTemplateConfig("", ",", false, true);
+
+    // Fragment expansion.
+    case '#':
+      *variable = variable->substr(1);
+      return UriTemplateConfig("#", ",", false, true);
+
+    // Label with dot-prefix.
+    case '.':
+      *variable = variable->substr(1);
+      return UriTemplateConfig(".", ".", false, false);
+
+    // Path segment expansion.
+    case '/':
+      *variable = variable->substr(1);
+      return UriTemplateConfig("/", "/", false, false);
+
+    // Path segment parameter expansion.
+    case ';':
+      *variable = variable->substr(1);
+      return UriTemplateConfig(";", ";", true, false, true);
+
+    // Form-style query expansion.
+    case '?':
+      *variable = variable->substr(1);
+      return UriTemplateConfig("?", "&", true, false);
+
+    // Form-style query continuation.
+    case '&':
+      *variable = variable->substr(1);
+      return UriTemplateConfig("&", "&", true, false);
+
+    // Simple expansion.
+    default:
+      return UriTemplateConfig("", ",", false, false);
+  }
+}
+
+void ProcessVariableSection(
+    string* variable_section,
+    const std::unordered_map<string, string>& parameters,
+    string* target,
+    std::set<string>* vars_found) {
+  // Note that this function will modify the variable_section string to remove
+  // the decorators, leaving just comma-separated variable name(s).
+  UriTemplateConfig config = MakeConfig(variable_section);
+  std::vector<string> variables = base::SplitString(
+      *variable_section, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  bool first_var = true;
+  for (const string& variable : variables) {
+    auto found = parameters.find(variable);
+    if (found != parameters.end()) {
+      config.AppendValue(variable, found->second, first_var, target);
+      first_var = false;
+      if (vars_found) {
+        vars_found->insert(variable);
+      }
+    }
+  }
+}
+
+}  // namespace
+
+bool Expand(const string& path_uri,
+            const std::unordered_map<string, string>& parameters,
+            string* target,
+            std::set<string>* vars_found) {
+  size_t cur = 0;
+  size_t length = path_uri.length();
+  while (cur < length) {
+    size_t open = path_uri.find('{', cur);
+    size_t close = path_uri.find('}', cur);
+    if (open == string::npos) {
+      if (close == string::npos) {
+        // No more variables to process.
+        target->append(path_uri.substr(cur).data(), path_uri.length() - cur);
+        return true;
+      } else {
+        // Template was malformed. Unexpected closing brace.
+        target->clear();
+        return false;
+      }
+    }
+    target->append(path_uri, cur, open - cur);
+    size_t next_open = path_uri.find('{', open + 1);
+    if (close == string::npos || close < open || next_open < close) {
+      // Template was malformed.
+      target->clear();
+      return false;
+    }
+    string variable_section(path_uri, open + 1, close - open - 1);
+    cur = close + 1;
+
+    ProcessVariableSection(&variable_section, parameters, target, vars_found);
+  }
+  return true;
+}
+
+}  // namespace uri_template
diff --git a/net/third_party/uri_template/uri_template.h b/net/third_party/uri_template/uri_template.h
new file mode 100644
index 0000000..00d7b9a
--- /dev/null
+++ b/net/third_party/uri_template/uri_template.h
@@ -0,0 +1,56 @@
+/*
+ * \copyright Copyright 2013 Google Inc. All Rights Reserved.
+ * \license @{
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @}
+ */
+
+#ifndef NET_THIRD_PARTY_URI_TEMPLATE_URI_TEMPLATE_H_
+#define NET_THIRD_PARTY_URI_TEMPLATE_URI_TEMPLATE_H_
+
+#include <set>
+#include <string>
+#include <unordered_map>
+
+#include "net/base/net_export.h"
+
+using std::string;
+
+namespace uri_template {
+
+/*
+ * Produce concrete URLs from templated ones. Collect the names of any expanded
+ * variables. Supports templates up to level 3 as specified in RFC 6570 with
+ * some limitations: it does not check for disallowed characters in variable
+ * names, and it does not do any encoding during literal expansion.
+ *
+ * @param[in] uri The templated uri to expand.
+ * @param[in] parameters A map containing variables and corresponding values.
+ * @param[out] target The string to resolve the templated uri into.
+ * @param[out] vars_found Populated with the list of variables names
+ *             that were resolved while expanding the uri. NULL is permitted
+ *             if the caller does not wish to collect these.
+ *
+ * @return true if the template was parseable. false if it was malformed.
+ */
+NET_EXPORT_PRIVATE bool Expand(
+    const string& template_uri,
+    const std::unordered_map<string, string>& parameters,
+    string* target,
+    std::set<string>* vars_found = NULL);
+
+}  // namespace uri_template
+
+#endif  // NET_THIRD_PARTY_URI_TEMPLATE_URI_TEMPLATE_H_
diff --git a/net/third_party/uri_template/uri_template_fuzzer.cc b/net/third_party/uri_template/uri_template_fuzzer.cc
new file mode 100644
index 0000000..302a21e
--- /dev/null
+++ b/net/third_party/uri_template/uri_template_fuzzer.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/uri_template/uri_template.h"
+
+#include "base/test/fuzzed_data_provider.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  base::FuzzedDataProvider fuzzed_data(data, size);
+  std::string uri_template = fuzzed_data.ConsumeRandomLengthString(256);
+  // Construct a map containing variable names and corresponding values.
+  std::unordered_map<std::string, std::string> parameters;
+  uint8_t num_vars(fuzzed_data.ConsumeUint8());
+  for (uint8_t i = 0; i < num_vars; i++) {
+    parameters.emplace(fuzzed_data.ConsumeRandomLengthString(10),
+                       fuzzed_data.ConsumeRandomLengthString(10));
+  }
+  std::string target;
+  uri_template::Expand(uri_template, parameters, &target);
+  return 0;
+}
diff --git a/net/third_party/uri_template/uri_template_test.cc b/net/third_party/uri_template/uri_template_test.cc
new file mode 100644
index 0000000..b2e6909e
--- /dev/null
+++ b/net/third_party/uri_template/uri_template_test.cc
@@ -0,0 +1,136 @@
+/*
+ * \copyright Copyright 2013 Google Inc. All Rights Reserved.
+ * \license @{
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @}
+ */
+
+#include "net/third_party/uri_template/uri_template.h"
+
+#include <memory>
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace uri_template {
+namespace {
+
+std::unordered_map<string, string> parameters_ = {
+    {"var", "value"},
+    {"hello", "Hello World!"},
+    {"path", "/foo/bar"},
+    {"empty", ""},
+    {"x", "1024"},
+    {"y", "768"},
+    {"percent", "%31"},
+    {"bad_percent", "%1"},
+    {"escaped", " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\x80\xFF"}};
+
+void CheckExpansion(const string& uri_template,
+                    const string& expected_expansion,
+                    bool expected_validity = true,
+                    const std::set<string>* expected_vars = NULL) {
+  string result;
+  std::set<string> vars_found;
+  EXPECT_EQ(expected_validity,
+            Expand(uri_template, parameters_, &result, &vars_found));
+  EXPECT_EQ(expected_expansion, result);
+  if (expected_vars) {
+    EXPECT_EQ(*expected_vars, vars_found);
+  }
+}
+
+class UriTemplateTest : public testing::Test {};
+
+TEST_F(UriTemplateTest, TestLevel1Templates) {
+  CheckExpansion("{var}", "value");
+  CheckExpansion("{hello}", "Hello%20World%21");
+  CheckExpansion("{percent}", "%2531");
+  CheckExpansion("{escaped}",
+                 "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F%3A%3B%3C%3D%3E%"
+                 "3F%40%5B%5C%5D%5E_%60%7B%7C%7D~%80%FF");
+}
+
+TEST_F(UriTemplateTest, TestLevel2Templates) {
+  // Reserved string expansion
+  CheckExpansion("{+var}", "value");
+  CheckExpansion("{+hello}", "Hello%20World!");
+  CheckExpansion("{+percent}", "%31");
+  CheckExpansion("{+bad_percent}", "%251");
+  CheckExpansion(
+      "{+escaped}",
+      "%20!%22#$%25&'()*+,-./:;%3C=%3E?@[%5C]%5E_%60%7B%7C%7D~%80%FF");
+  CheckExpansion("{+path}/here", "/foo/bar/here");
+  CheckExpansion("here?ref={+path}", "here?ref=/foo/bar");
+  // Fragment expansion
+  CheckExpansion("X{#var}", "X#value");
+  CheckExpansion("X{#hello}", "X#Hello%20World!");
+}
+
+TEST_F(UriTemplateTest, TestLevel3Templates) {
+  // String expansion with multiple variables
+  CheckExpansion("map?{x,y}", "map?1024,768");
+  CheckExpansion("{x,hello,y}", "1024,Hello%20World%21,768");
+  // Reserved expansion with multiple variables
+  CheckExpansion("{+x,hello,y}", "1024,Hello%20World!,768");
+  CheckExpansion("{+path,x}/here", "/foo/bar,1024/here");
+  // Fragment expansion with multiple variables
+  CheckExpansion("{#x,hello,y}", "#1024,Hello%20World!,768");
+  CheckExpansion("{#path,x}/here", "#/foo/bar,1024/here");
+  // Label expansion, dot-prefixed
+  CheckExpansion("X{.var}", "X.value");
+  CheckExpansion("X{.x,y}", "X.1024.768");
+  // Path segments, slash-prefixed
+  CheckExpansion("{/var}", "/value");
+  CheckExpansion("{/var,x}/here", "/value/1024/here");
+  // Path-style parameters, semicolon-prefixed
+  CheckExpansion("{;x,y}", ";x=1024;y=768");
+  CheckExpansion("{;x,y,empty}", ";x=1024;y=768;empty");
+  // Form-style query, ampersand-separated
+  CheckExpansion("{?x,y}", "?x=1024&y=768");
+  CheckExpansion("{?x,y,empty}", "?x=1024&y=768&empty=");
+  // Form-style query continuation
+  CheckExpansion("?fixed=yes{&x}", "?fixed=yes&x=1024");
+  CheckExpansion("{&x,y,empty}", "&x=1024&y=768&empty=");
+}
+
+TEST_F(UriTemplateTest, TestMalformed) {
+  CheckExpansion("{", "", false);
+  CheckExpansion("map?{x", "", false);
+  CheckExpansion("map?{x,{y}", "", false);
+  CheckExpansion("map?{x,y}}", "", false);
+  CheckExpansion("map?{{x,y}}", "", false);
+}
+
+TEST_F(UriTemplateTest, TestVariableSet) {
+  std::set<string> expected_vars = {};
+  CheckExpansion("map?{z}", "map?", true, &expected_vars);
+  CheckExpansion("map{?z}", "map", true, &expected_vars);
+  expected_vars = {"empty"};
+  CheckExpansion("{empty}", "", true, &expected_vars);
+  expected_vars = {"x", "y"};
+  CheckExpansion("map?{x,y}", "map?1024,768", true, &expected_vars);
+  CheckExpansion("map?{x,z,y}", "map?1024,768", true, &expected_vars);
+  CheckExpansion("map{?x,z,y}", "map?x=1024&y=768", true, &expected_vars);
+  expected_vars = {"y", "path"};
+  CheckExpansion("{+path}{/z}{?y}&k=24", "/foo/bar?y=768&k=24", true,
+                 &expected_vars);
+  CheckExpansion("{y}{+path}", "768/foo/bar", true, &expected_vars);
+}
+
+}  // namespace
+}  // namespace uri_template
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 0f5fe6f..68d69df 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1074,21 +1074,30 @@
 
 bool URLRequest::CanGetCookies(const CookieList& cookie_list) const {
   DCHECK(!(load_flags_ & LOAD_DO_NOT_SEND_COOKIES));
+  bool can_get_cookies = g_default_can_use_cookies;
   if (network_delegate_) {
-    return network_delegate_->CanGetCookies(*this, cookie_list,
-                                            /*allowed_from_caller=*/true);
+    can_get_cookies =
+        network_delegate_->CanGetCookies(*this, cookie_list,
+                                         /*allowed_from_caller=*/true);
   }
-  return g_default_can_use_cookies;
+
+  if (!can_get_cookies)
+    net_log_.AddEvent(NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE);
+  return can_get_cookies;
 }
 
 bool URLRequest::CanSetCookie(const net::CanonicalCookie& cookie,
                               CookieOptions* options) const {
   DCHECK(!(load_flags_ & LOAD_DO_NOT_SAVE_COOKIES));
+  bool can_set_cookies = g_default_can_use_cookies;
   if (network_delegate_) {
-    return network_delegate_->CanSetCookie(*this, cookie, options,
-                                           /*allowed_from_caller=*/true);
+    can_set_cookies =
+        network_delegate_->CanSetCookie(*this, cookie, options,
+                                        /*allowed_from_caller=*/true);
   }
-  return g_default_can_use_cookies;
+  if (!can_set_cookies)
+    net_log_.AddEvent(NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE);
+  return can_set_cookies;
 }
 
 bool URLRequest::CanEnablePrivacyMode() const {
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index dd95f0fd..19b71c4 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2740,6 +2740,12 @@
 
     EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
     EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
+    TestNetLogEntry::List entries;
+    net_log_.GetEntries(&entries);
+    for (const auto& entry : entries) {
+      EXPECT_NE(entry.type,
+                NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE);
+    }
   }
 
   // Verify that the cookie isn't sent.
@@ -2759,6 +2765,11 @@
 
     EXPECT_EQ(1, network_delegate.blocked_get_cookies_count());
     EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
+    TestNetLogEntry::List entries;
+    net_log_.GetEntries(&entries);
+    ExpectLogContainsSomewhereAfter(
+        entries, 0, NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE,
+        NetLogEventPhase::NONE);
   }
 }
 
@@ -2785,6 +2796,12 @@
 
     EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
     EXPECT_EQ(0, network_delegate.blocked_set_cookie_count());
+    TestNetLogEntry::List entries;
+    net_log_.GetEntries(&entries);
+    for (const auto& entry : entries) {
+      EXPECT_NE(entry.type,
+                NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE);
+    }
   }
 
   // Try to set-up another cookie and update the previous cookie.
@@ -2802,6 +2819,11 @@
 
     EXPECT_EQ(0, network_delegate.blocked_get_cookies_count());
     EXPECT_EQ(2, network_delegate.blocked_set_cookie_count());
+    TestNetLogEntry::List entries;
+    net_log_.GetEntries(&entries);
+    ExpectLogContainsSomewhereAfter(
+        entries, 0, NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE,
+        NetLogEventPhase::NONE);
   }
 
   // Verify the cookies weren't saved or updated.
diff --git a/ppapi/host/resource_message_filter.cc b/ppapi/host/resource_message_filter.cc
index cea9f9f..2821b8d7 100644
--- a/ppapi/host/resource_message_filter.cc
+++ b/ppapi/host/resource_message_filter.cc
@@ -54,7 +54,7 @@
 }
 
 void ResourceMessageFilter::OnFilterDestroyed() {
-  resource_host_ = NULL;
+  resource_host_ = nullptr;
 }
 
 bool ResourceMessageFilter::HandleMessage(const IPC::Message& msg,
diff --git a/ppapi/host/resource_message_filter.h b/ppapi/host/resource_message_filter.h
index 42658ea..932d29e 100644
--- a/ppapi/host/resource_message_filter.h
+++ b/ppapi/host/resource_message_filter.h
@@ -92,7 +92,7 @@
   // Called when a filter is added to a ResourceHost.
   void OnFilterAdded(ResourceHost* resource_host);
   // Called when a filter is removed from a ResourceHost.
-  void OnFilterDestroyed();
+  virtual void OnFilterDestroyed();
 
   // This will dispatch the message handler on the target thread. It returns
   // true if the message was handled by this filter and false otherwise.
diff --git a/ppapi/proxy/udp_socket_filter.cc b/ppapi/proxy/udp_socket_filter.cc
index ae96c894..8563286 100644
--- a/ppapi/proxy/udp_socket_filter.cc
+++ b/ppapi/proxy/udp_socket_filter.cc
@@ -226,11 +226,11 @@
     if (static_cast<size_t>(num_bytes) < front.data.size())
       return PP_ERROR_MESSAGE_TOO_BIG;
 
-    int32_t result = static_cast<int32_t>(front.data.size());
     std::unique_ptr<std::string> data_to_pass(new std::string);
     data_to_pass->swap(front.data);
-    SetRecvFromOutput(pp_instance_, std::move(data_to_pass), front.addr,
-                      buffer_out, num_bytes, addr_out, PP_OK);
+    int32_t result =
+        SetRecvFromOutput(pp_instance_, std::move(data_to_pass), front.addr,
+                          buffer_out, num_bytes, addr_out, front.result);
     last_recvfrom_addr_ = front.addr;
     recv_buffers_.pop();
     slot_available_callback_.Run();
diff --git a/ppapi/tests/test_udp_socket.cc b/ppapi/tests/test_udp_socket.cc
index b82019e5..29c422b2 100644
--- a/ppapi/tests/test_udp_socket.cc
+++ b/ppapi/tests/test_udp_socket.cc
@@ -104,6 +104,14 @@
   RUN_CALLBACK_TEST(TestUDPSocket, SetOption, filter);
   RUN_CALLBACK_TEST(TestUDPSocket, ParallelSend, filter);
   RUN_CALLBACK_TEST(TestUDPSocket, Multicast, filter);
+
+  // Failure tests. Generally can only be run individually, since they require
+  // specific socket failures to be injected into the UDP code.
+  RUN_CALLBACK_TEST(TestUDPSocket, BindFails, filter);
+  RUN_CALLBACK_TEST(TestUDPSocket, BroadcastBeforeBindFails, filter);
+  RUN_CALLBACK_TEST(TestUDPSocket, BroadcastAfterBindFails, filter);
+  RUN_CALLBACK_TEST(TestUDPSocket, SendToFails, filter);
+  RUN_CALLBACK_TEST(TestUDPSocket, ReadFails, filter);
 }
 
 std::string TestUDPSocket::GetLocalAddress(pp::NetAddress* address) {
@@ -516,3 +524,93 @@
 
   PASS();
 }
+
+std::string TestUDPSocket::TestBindFails() {
+  pp::UDPSocket socket(instance_);
+
+  PP_NetAddress_IPv4 any_ipv4_address = {0, {0, 0, 0, 0}};
+  pp::NetAddress any_address(instance_, any_ipv4_address);
+  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+  callback.WaitForResult(socket.Bind(any_address, callback.GetCallback()));
+  CHECK_CALLBACK_BEHAVIOR(callback);
+  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
+  PASS();
+}
+
+std::string TestUDPSocket::TestBroadcastBeforeBindFails() {
+  pp::UDPSocket socket(instance_);
+  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&socket));
+
+  PP_NetAddress_IPv4 any_ipv4_address = {0, {0, 0, 0, 0}};
+  pp::NetAddress any_address(instance_, any_ipv4_address);
+  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+  callback.WaitForResult(socket.Bind(any_address, callback.GetCallback()));
+  CHECK_CALLBACK_BEHAVIOR(callback);
+  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
+  PASS();
+}
+
+std::string TestUDPSocket::TestBroadcastAfterBindFails() {
+  pp::UDPSocket socket(instance_);
+  PP_NetAddress_IPv4 any_ipv4_address = {0, {0, 0, 0, 0}};
+  pp::NetAddress any_address(instance_, any_ipv4_address);
+  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&socket, any_address));
+
+  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
+  callback.WaitForResult(socket.SetOption(
+      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback.GetCallback()));
+  CHECK_CALLBACK_BEHAVIOR(callback);
+  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
+
+  // Setting broadcast again should also fail.
+  TestCompletionCallback callback_2(instance_->pp_instance(), callback_type());
+  callback_2.WaitForResult(socket.SetOption(
+      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback_2.GetCallback()));
+  CHECK_CALLBACK_BEHAVIOR(callback_2);
+  ASSERT_EQ(PP_ERROR_FAILED, callback_2.result());
+  PASS();
+}
+
+std::string TestUDPSocket::TestSendToFails() {
+  pp::UDPSocket socket(instance_);
+  PP_NetAddress_IPv4 any_ipv4_address = {0, {0, 0, 0, 0}};
+  pp::NetAddress any_address(instance_, any_ipv4_address);
+  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&socket, any_address));
+
+  std::vector<char> buffer(1);
+  buffer[0] = 1;
+  PP_NetAddress_IPv4 target_ipv4_address = {1024, {127, 0, 0, 1}};
+  pp::NetAddress target_address(instance_, target_ipv4_address);
+  // All writes should fail.
+  for (int i = 0; i < 10; ++i) {
+    TestCompletionCallbackWithOutput<pp::NetAddress> callback(
+        instance_->pp_instance(), callback_type());
+    callback.WaitForResult(
+        socket.SendTo(buffer.data(), static_cast<int32_t>(buffer.size()),
+                      target_address, callback.GetCallback()));
+    CHECK_CALLBACK_BEHAVIOR(callback);
+    ASSERT_EQ(PP_ERROR_FAILED, callback.result());
+  }
+  PASS();
+}
+
+std::string TestUDPSocket::TestReadFails() {
+  pp::UDPSocket socket(instance_);
+  PP_NetAddress_IPv4 any_ipv4_address = {0, {0, 0, 0, 0}};
+  pp::NetAddress any_address(instance_, any_ipv4_address);
+  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&socket, any_address));
+
+  std::vector<char> buffer(1);
+  // All reads should fail. Larger number of reads increases the chance that at
+  // least one read will be synchronous.
+  for (int i = 0; i < 200; ++i) {
+    TestCompletionCallbackWithOutput<pp::NetAddress> callback(
+        instance_->pp_instance(), callback_type());
+    callback.WaitForResult(socket.RecvFrom(&buffer[0],
+                                           static_cast<int32_t>(buffer.size()),
+                                           callback.GetCallback()));
+    CHECK_CALLBACK_BEHAVIOR(callback);
+    ASSERT_EQ(PP_ERROR_FAILED, callback.result());
+  }
+  PASS();
+}
diff --git a/ppapi/tests/test_udp_socket.h b/ppapi/tests/test_udp_socket.h
index 5307b39..0ac8482d 100644
--- a/ppapi/tests/test_udp_socket.h
+++ b/ppapi/tests/test_udp_socket.h
@@ -63,6 +63,14 @@
   std::string TestParallelSend();
   std::string TestMulticast();
 
+  // Error cases. It's up to the parent test fixture to ensure that these events
+  // result in errors.
+  std::string TestBindFails();
+  std::string TestBroadcastBeforeBindFails();
+  std::string TestBroadcastAfterBindFails();
+  std::string TestSendToFails();
+  std::string TestReadFails();
+
   pp::NetAddress address_;
 
   const PPB_UDPSocket_1_0* socket_interface_1_0_;
diff --git a/services/audio/input_controller.cc b/services/audio/input_controller.cc
index a07e16d2..6e0df4f 100644
--- a/services/audio/input_controller.cc
+++ b/services/audio/input_controller.cc
@@ -438,7 +438,6 @@
 
   UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS);
 
-  UMA_HISTOGRAM_PERCENTAGE("Media.MicrophoneVolume", microphone_volume_percent);
   log_string = base::StringPrintf("AIC::OnData: microphone volume=%d%%",
                                   microphone_volume_percent);
   if (microphone_volume_percent < kLowLevelMicrophoneLevelPercent)
diff --git a/services/device/device_service.cc b/services/device/device_service.cc
index 0c55aa9..7be23bc 100644
--- a/services/device/device_service.cc
+++ b/services/device/device_service.cc
@@ -110,11 +110,6 @@
 #if !defined(OS_ANDROID)
   device::BatteryStatusService::GetInstance()->Shutdown();
 #endif
-  if (public_ip_address_geolocation_provider_) {
-    DCHECK(io_task_runner_);
-    io_task_runner_->DeleteSoon(
-        FROM_HERE, std::move(public_ip_address_geolocation_provider_));
-  }
 }
 
 void DeviceService::OnStart() {
@@ -130,8 +125,7 @@
       &DeviceService::BindPowerMonitorRequest, base::Unretained(this)));
   registry_.AddInterface<mojom::PublicIpAddressGeolocationProvider>(
       base::Bind(&DeviceService::BindPublicIpAddressGeolocationProviderRequest,
-                 base::Unretained(this)),
-      io_task_runner_);
+                 base::Unretained(this)));
   registry_.AddInterface<mojom::ScreenOrientationListener>(
       base::Bind(&DeviceService::BindScreenOrientationListenerRequest,
                  base::Unretained(this)));
@@ -255,16 +249,11 @@
 
 void DeviceService::BindPublicIpAddressGeolocationProviderRequest(
     mojom::PublicIpAddressGeolocationProviderRequest request) {
-  // This needs to run on IO thread because it uses URLRequestContextGetters
-  // which must not outlive that thread.
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-
   if (!public_ip_address_geolocation_provider_) {
     public_ip_address_geolocation_provider_ =
         std::make_unique<PublicIpAddressGeolocationProvider>(
             url_loader_factory_, geolocation_api_key_);
   }
-
   public_ip_address_geolocation_provider_->Bind(std::move(request));
 }
 
diff --git a/services/network/ignore_errors_cert_verifier_unittest.cc b/services/network/ignore_errors_cert_verifier_unittest.cc
index 21aecfa8..7bdb9a5 100644
--- a/services/network/ignore_errors_cert_verifier_unittest.cc
+++ b/services/network/ignore_errors_cert_verifier_unittest.cc
@@ -94,8 +94,7 @@
 
 static CertVerifier::RequestParams MakeRequestParams(
     const scoped_refptr<X509Certificate>& cert) {
-  return CertVerifier::RequestParams(cert, "example.com", 0, "",
-                                     net::CertificateList());
+  return CertVerifier::RequestParams(cert, "example.com", 0, "");
 }
 
 static void GetWhitelistedTestCert(scoped_refptr<X509Certificate>* out) {
diff --git a/services/network/proxy_resolving_client_socket.cc b/services/network/proxy_resolving_client_socket.cc
index 145ba1ca..a0b7a9b 100644
--- a/services/network/proxy_resolving_client_socket.cc
+++ b/services/network/proxy_resolving_client_socket.cc
@@ -74,7 +74,8 @@
 int ProxyResolvingClientSocket::CancelReadIfReady() {
   if (socket_handle_->socket())
     return socket_handle_->socket()->CancelReadIfReady();
-  return net::ERR_SOCKET_NOT_CONNECTED;
+  // Return net::OK as ReadIfReady() is canceled when socket is disconnected.
+  return net::OK;
 }
 
 int ProxyResolvingClientSocket::Write(
diff --git a/services/network/proxy_resolving_socket_mojo_unittest.cc b/services/network/proxy_resolving_socket_mojo_unittest.cc
index aa8c795..d9cfd0e 100644
--- a/services/network/proxy_resolving_socket_mojo_unittest.cc
+++ b/services/network/proxy_resolving_socket_mojo_unittest.cc
@@ -211,7 +211,7 @@
     base::RunLoop().RunUntilIdle();
     if (!is_direct) {
       EXPECT_EQ(net::IPEndPoint(), actual_remote_addr);
-      EXPECT_FALSE(socket_data.AllReadDataConsumed());
+      EXPECT_TRUE(socket_data.AllReadDataConsumed());
       EXPECT_TRUE(socket_data.AllWriteDataConsumed());
     } else {
       EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
diff --git a/services/network/socket_data_pump.cc b/services/network/socket_data_pump.cc
index d0e70a43..75f18f5e 100644
--- a/services/network/socket_data_pump.cc
+++ b/services/network/socket_data_pump.cc
@@ -81,6 +81,7 @@
       buf.get(), base::saturated_cast<int>(num_bytes),
       base::BindRepeating(&SocketDataPump::OnNetworkReadIfReadyCompleted,
                           weak_factory_.GetWeakPtr()));
+  DCHECK_NE(net::ERR_READ_IF_READY_NOT_IMPLEMENTED, read_result);
   receive_stream_ =
       pending_receive_buffer->Complete(read_result >= 0 ? read_result : 0);
   if (read_result == net::ERR_IO_PENDING) {
diff --git a/services/network/ssl_config_service_mojo_unittest.cc b/services/network/ssl_config_service_mojo_unittest.cc
index 18fb5648..6259c9f 100644
--- a/services/network/ssl_config_service_mojo_unittest.cc
+++ b/services/network/ssl_config_service_mojo_unittest.cc
@@ -503,8 +503,7 @@
   net::CertVerifyResult cert_verify_result1;
   std::unique_ptr<net::CertVerifier::Request> request1;
   int result = network_context_->url_request_context()->cert_verifier()->Verify(
-      net::CertVerifier::RequestParams(cert, "127.0.0.1", 0, std::string(),
-                                       net::CertificateList()),
+      net::CertVerifier::RequestParams(cert, "127.0.0.1", 0, std::string()),
       &cert_verify_result1, callback1.callback(), &request1,
       net::NetLogWithSource());
   ASSERT_THAT(callback1.GetResult(result), net::test::IsOk());
@@ -525,8 +524,7 @@
   net::CertVerifyResult cert_verify_result2;
   std::unique_ptr<net::CertVerifier::Request> request2;
   result = network_context_->url_request_context()->cert_verifier()->Verify(
-      net::CertVerifier::RequestParams(cert, "127.0.0.1", 0, std::string(),
-                                       net::CertificateList()),
+      net::CertVerifier::RequestParams(cert, "127.0.0.1", 0, std::string()),
       &cert_verify_result2, callback2.callback(), &request2,
       net::NetLogWithSource());
   ASSERT_THAT(callback2.GetResult(result),
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 393f1bae..725746e 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -778,9 +778,9 @@
   if (num_bytes > 0) {
     pending_write_buffer_offset_ += num_bytes;
 
-    // Only notify client of download progress in case DevTools are attached
-    // and we're done sniffing and started sending response.
-    if (report_raw_headers_ && !consumer_handle_.is_valid()) {
+    // Only notify client of download progress if we're done sniffing and
+    // started sending response.
+    if (!consumer_handle_.is_valid()) {
       int64_t total_encoded_bytes = url_request_->GetTotalReceivedBytes();
       int64_t delta = total_encoded_bytes - reported_total_encoded_bytes_;
       DCHECK_LE(0, delta);
diff --git a/services/preferences/tracked/pref_hash_filter_unittest.cc b/services/preferences/tracked/pref_hash_filter_unittest.cc
index af1ab1c8..9e3a6a8 100644
--- a/services/preferences/tracked/pref_hash_filter_unittest.cc
+++ b/services/preferences/tracked/pref_hash_filter_unittest.cc
@@ -251,6 +251,8 @@
     const std::string& path,
     const base::DictionaryValue* split_values) {
   std::unique_ptr<base::DictionaryValue> macs_dict(new base::DictionaryValue);
+  if (!split_values)
+    return macs_dict;
   for (base::DictionaryValue::Iterator it(*split_values); !it.IsAtEnd();
        it.Advance()) {
     macs_dict->SetKey(it.key(),
@@ -694,6 +696,27 @@
   VerifyRecordedReset(false);
 }
 
+TEST_P(PrefHashFilterTest, FilterTrackedPrefClearing) {
+  base::DictionaryValue root_dict;
+  // We don't actually add the pref's value to root_dict to simulate that
+  // it was just cleared in the PrefStore.
+
+  // No path should be stored on FilterUpdate.
+  pref_hash_filter_->FilterUpdate(kAtomicPref);
+  ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
+
+  // One path should be stored on FilterSerializeData, with no value.
+  pref_hash_filter_->FilterSerializeData(&root_dict);
+  ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths_count());
+  MockPrefHashStore::ValuePtrStrategyPair stored_value =
+      mock_pref_hash_store_->stored_value(kAtomicPref);
+  ASSERT_EQ(nullptr, stored_value.first);
+  ASSERT_EQ(PrefTrackingStrategy::ATOMIC, stored_value.second);
+
+  ASSERT_EQ(1u, mock_pref_hash_store_->transactions_performed());
+  VerifyRecordedReset(false);
+}
+
 TEST_P(PrefHashFilterTest, ReportSuperMacValidity) {
   // Do this once just to force the histogram to be defined.
   DoFilterOnLoad(false);
@@ -753,6 +776,27 @@
   VerifyRecordedReset(false);
 }
 
+TEST_P(PrefHashFilterTest, FilterTrackedSplitPrefClearing) {
+  base::DictionaryValue root_dict;
+  // We don't actually add the pref's value to root_dict to simulate that
+  // it was just cleared in the PrefStore.
+
+  // No path should be stored on FilterUpdate.
+  pref_hash_filter_->FilterUpdate(kSplitPref);
+  ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths_count());
+
+  // One path should be stored on FilterSerializeData, with no value.
+  pref_hash_filter_->FilterSerializeData(&root_dict);
+  ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths_count());
+  MockPrefHashStore::ValuePtrStrategyPair stored_value =
+      mock_pref_hash_store_->stored_value(kSplitPref);
+  ASSERT_EQ(nullptr, stored_value.first);
+  ASSERT_EQ(PrefTrackingStrategy::SPLIT, stored_value.second);
+
+  ASSERT_EQ(1u, mock_pref_hash_store_->transactions_performed());
+  VerifyRecordedReset(false);
+}
+
 TEST_P(PrefHashFilterTest, FilterUntrackedPrefUpdate) {
   base::DictionaryValue root_dict;
   root_dict.SetString("untracked", "some value");
diff --git a/services/preferences/tracked/pref_hash_store_impl.cc b/services/preferences/tracked/pref_hash_store_impl.cc
index e38697d..477996997 100644
--- a/services/preferences/tracked/pref_hash_store_impl.cc
+++ b/services/preferences/tracked/pref_hash_store_impl.cc
@@ -97,7 +97,8 @@
 std::unique_ptr<base::DictionaryValue> PrefHashStoreImpl::ComputeSplitMacs(
     const std::string& path,
     const base::DictionaryValue* split_values) {
-  DCHECK(split_values);
+  if (!split_values)
+    return std::make_unique<base::DictionaryValue>();
 
   std::string keyed_path(path);
   keyed_path.push_back('.');
diff --git a/services/preferences/tracked/pref_hash_store_impl_unittest.cc b/services/preferences/tracked/pref_hash_store_impl_unittest.cc
index 62c8ae2..64d73549 100644
--- a/services/preferences/tracked/pref_hash_store_impl_unittest.cc
+++ b/services/preferences/tracked/pref_hash_store_impl_unittest.cc
@@ -77,6 +77,15 @@
       mac_3);
 }
 
+TEST_F(PrefHashStoreImplTest, ComputeNullSplitMacs) {
+  PrefHashStoreImpl pref_hash_store(std::string(32, 0), "device_id", true);
+  std::unique_ptr<base::DictionaryValue> computed_macs =
+      pref_hash_store.ComputeSplitMacs("foo.bar", nullptr);
+
+  ASSERT_TRUE(computed_macs);
+  EXPECT_TRUE(computed_macs->empty());
+}
+
 TEST_F(PrefHashStoreImplTest, AtomicHashStoreAndCheck) {
   base::Value string_1("string1");
   base::Value string_2("string2");
diff --git a/services/ui/ws2/topmost_window_observer.cc b/services/ui/ws2/topmost_window_observer.cc
index 9e81faa..34124fd7 100644
--- a/services/ui/ws2/topmost_window_observer.cc
+++ b/services/ui/ws2/topmost_window_observer.cc
@@ -7,6 +7,7 @@
 #include "services/ui/ws2/window_service.h"
 #include "services/ui/ws2/window_service_delegate.h"
 #include "services/ui/ws2/window_tree.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
@@ -43,10 +44,10 @@
     ::wm::ConvertPointToScreen(root_, &last_location_);
   } else {
     gfx::PointF point;
-    if (ui::GestureRecognizer::Get()->GetLastTouchPointForTarget(last_target_,
-                                                                 &point)) {
+    ui::GestureRecognizer* gesture_recognizer =
+        initial_target->env()->gesture_recognizer();
+    if (gesture_recognizer->GetLastTouchPointForTarget(last_target_, &point))
       last_location_ = gfx::Point(point.x(), point.y());
-    }
     ::wm::ConvertPointToScreen(last_target_, &last_location_);
   }
   UpdateTopmostWindows();
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 3065dfa..0c6fadc 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -73,7 +73,6 @@
 
 # https://crbug.com/816684 Track Page Load Metrics.
 -PageLoadMetricsBrowserTest.LoadingMetricsFailed
--PageLoadMetricsBrowserTest.ReceivedCompleteResources
 
 # https://crbug.com/810329 DnsProbe browsertests that rely on delaying requests:
 -DnsProbeBrowserTest.NxdomainProbeResultWithWorkingSlowCorrections
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 42667f4..940dd04 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3083,6 +3083,25 @@
             ]
         }
     ],
+    "PassiveDocumentWheelEventListeners": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "PassiveDocumentWheelEventListeners"
+                    ]
+                }
+            ]
+        }
+    ],
     "PassiveEventListenersDueToFling": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
index 1df8ec2..242ee03b 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
@@ -131,6 +131,8 @@
 crbug.com/865039 compositing/masks/mask-with-added-filters.html [ Pass ]
 # Link highlights are no longer affected by ancestor effects.
 crbug.com/857501 compositing/gestures/gesture-tapHighlight-with-filter.html [ Pass ]
+crbug.com/400829 media/video-object-fit.html [ Pass ]
+crbug.com/400829 compositing/composited-canvas-with-overflowing-object-fit.html [ Pass ]
 
 # Backdrop filters are experimental (behind a flag) and do not really work with
 # or without BlinkGenPropertyTrees. These tests are marked as failing because
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index eebd13d2..bd62cc5b 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2186,6 +2186,8 @@
 crbug.com/400829 virtual/video-surface-layer/media/video-object-fit.html [ Failure ]
 crbug.com/400829 virtual/stable/media/stable/video-object-fit-stable.html [ Failure ]
 crbug.com/400829 virtual/video-surface-layer/media/stable/video-object-fit-stable.html [ Failure ]
+crbug.com/400829 compositing/composited-canvas-with-overflowing-object-fit.html [ Failure ]
+crbug.com/400829 virtual/gpu/fast/canvas/canvas-with-overflowing-object-fit.html [ Failure ]
 
 # We only want to run one of the web-animations-api tests in stable mode.
 crbug.com/441553 virtual/stable/web-animations-api [ Skip ]
@@ -2719,6 +2721,7 @@
 crbug.com/849859 external/wpt/web-animations/timing-model/animations/pausing-an-animation.html [ Failure ]
 crbug.com/875622 external/wpt/css/css-animations/AnimationEffect-getComputedTiming.tentative.html [ Pass Failure ]
 crbug.com/875622 external/wpt/css/css-transitions/AnimationEffect-getComputedTiming.tentative.html [ Pass Failure ]
+crbug.com/875622 external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html [ Pass Failure Timeout ]
 
 # wptserve raises exceptions when running this test:
 crbug.com/870526 external/wpt/background-fetch/idlharness.https.any.serviceworker.html [ Skip ]
@@ -2729,8 +2732,9 @@
 crbug.com/875249 external/wpt/infrastructure/testdriver/bless.html [ Timeout Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html [ Timeout ]
+crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html [ Failure ]
 crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-transitions/event-dispatch.tentative.html [ Timeout ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-002.svg [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-shape-inside-001.svg [ Failure ]
@@ -2827,9 +2831,6 @@
 crbug.com/626703 external/wpt/html/editing/focus/sequential-focus-navigation-and-the-tabindex-attribute/focus-tabindex-zero.html [ Timeout ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-column-row-gap-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-column-row-gap-002.html [ Failure ]
-crbug.com/626703 virtual/outofblink-cors/external/wpt/fetch/cross-origin-resource-policy/fetch.any.html [ Timeout ]
-crbug.com/626703 virtual/outofblink-cors-ns/external/wpt/fetch/cross-origin-resource-policy/fetch.any.html [ Timeout ]
-crbug.com/626703 external/wpt/fetch/cross-origin-resource-policy/fetch.any.html [ Timeout ]
 crbug.com/626703 virtual/outofblink-cors/external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Timeout ]
 crbug.com/626703 virtual/outofblink-cors-ns/external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Timeout ]
 crbug.com/626703 external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Timeout ]
@@ -3955,15 +3956,9 @@
 crbug.com/722943 virtual/video-surface-layer/media/audio-repaint.html [ Skip ]
 
 # Sheriff failures 2018-08-13
-crbug.com/873547 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-event-fired.html [ Timeout Pass ]
-crbug.com/873547 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-event-fired.html [ Timeout Pass ]
 crbug.com/873454 css3/filters/effect-reference-image-hw.html [ Failure Pass ]
 
-# Sheriff failures 2018-08-14
-crbug.com/873435 virtual/user-activation-v2/fast/events/middleClickAutoscroll-in-iframe.html [ Timeout Pass ]
-
 # Sheriff failures 2018-08-15
-crbug.com/873435 [ Mac ] fast/events/middleClickAutoscroll-in-iframe.html [ Timeout Pass ]
 crbug.com/869726 [ Mac ] fast/events/touch/touch-latched-scroll-node-removed.html [ Pass Failure ]
 crbug.com/869726 [ Mac ] virtual/user-activation-v2/fast/events/touch/touch-latched-scroll-node-removed.html [ Pass Failure ]
 crbug.com/869726 [ Mac ] fast/events/wheel/wheel-latched-scroll-node-removed.html [ Pass Failure ]
@@ -4547,10 +4542,9 @@
 
 # Sheriff 2018-03-29
 crbug.com/827088 bluetooth/requestDevice/chooser/new-scan-device-added.html [ Pass Crash ]
-crbug.com/827209 fast/events/middleClickAutoscroll-latching.html [ Pass Failure Timeout ]
-crbug.com/827209 virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-latching.html [ Pass Failure Timeout ]
-# Sheriff 2018-08-14
-crbug.com/827209 virtual/user-activation-v2/fast/events/middleClickAutoscroll-latching.html [ Pass Timeout Failure ]
+crbug.com/827209 [ Win Linux ] fast/events/middleClickAutoscroll-latching.html [ Pass Failure ]
+crbug.com/827209 [ Win Linux ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-latching.html [ Pass Failure ]
+crbug.com/827209 [ Win Linux ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-latching.html [ Pass Failure ]
 
 # Utility for manual testing, not intended to be run as part of layout tests.
 crbug.com/785955 http/tests/credentialmanager/tools/virtual-authenticator-environment-manual.html [ Skip ]
@@ -4681,7 +4675,7 @@
 
 # Sheriff 2018-06-08
 crbug.com/850170 virtual/reporting-api/external/wpt/content-security-policy/reporting-api/reporting-api-doesnt-send-reports-without-violation.https.sub.html [ Pass Crash Timeout ]
-crbug.com/851090 virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-click-hyperlink.html [ Pass Failure ]
+crbug.com/851090 [ Win Linux ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-click-hyperlink.html [ Pass Failure ]
 
 # Sheriff 2018-06-11
 crbug.com/850202 [ Linux ] http/tests/devtools/network/network-filters.js [ Pass Failure Timeout ]
@@ -4784,8 +4778,6 @@
 
 crbug.com/867532 [ Linux ] external/wpt/workers/modules/dedicated-worker-import-data-url.any.worker.html [ Timeout Pass ]
 crbug.com/867532 [ Linux ] external/wpt/workers/modules/dedicated-worker-import.any.worker.html [ Timeout Pass ]
-crbug.com/867628 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-click-hyperlink.html [ Failure Pass ]
-crbug.com/867628 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-nested-divs-forbidden.html [ Timeout Pass Failure ]
 
 # Sheriff 2018-07-26
 crbug.com/867376 [ Mac Linux ] css3/filters/filter-repaint-composited-fallback-crash.html [ Timeout Pass ]
@@ -4942,33 +4934,17 @@
 crbug.com/871578 [ Mac ] virtual/outofblink-cors/external/wpt/xhr/timeout-multiple-fetches.html [ Failure Pass ]
 crbug.com/871578 [ Mac ] virtual/outofblink-cors-ns/external/wpt/xhr/timeout-multiple-fetches.html [ Failure Pass ]
 
-# Flaky middleClinkAutoscroll increased flakiness on Mac
-crbug.com/851090 [ Mac ] fast/events/middleClickAutoscroll-click-hyperlink.html [ Failure Pass ]
-crbug.com/851090 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-nested-divs.html [ Failure Pass Timeout ]
-crbug.com/851090 [ Mac ] fast/events/middleClickAutoscroll-nested-divs.html [ Failure Pass Timeout ]
-crbug.com/851090 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-over-scrollbar.html [ Failure Pass ]
-
 # Sheriff 2018-08-08
 crbug.com/871105 [ Linux ] svg/dom/remove-use-target-element-indirectly.html [ Pass Crash ]
 crbug.com/872025 [ Mac Linux ] fast/css-grid-layout/crash-large-positions.html [ Pass Timeout ]
 crbug.com/871416 fast/spatial-navigation/snav-stay-in-overflow-div.html [ Pass Failure ]
-crbug.com/872242 [ Mac ] fast/events/autoscroll-over-scrollbar.html [ Pass Failure ]
 
 # Only passes on bots with GPUs.
 crbug.com/871445 [ Mac ] http/tests/media/video-load-metadata-decode-error.html [ Timeout ]
 
 # Sheriff 2018-08-09
-crbug.com/872635 [ Mac ] fast/events/middleClickAutoscroll-nested-divs-forbidden.html [ Failure Pass ]
-crbug.com/872705 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-over-scrollbar.html [ Failure Pass ]
 crbug.com/872705 [ Mac ] virtual/mouseevent_fractional/fast/events/recorded-keydown-event.html [ Timeout Pass ]
 
-# Sheriff 2018-08-10
-crbug.com/872705 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-click.html [ Timeout Pass ]
-crbug.com/872705 [ Mac ] fast/events/middleClickAutoscroll-click.html [ Timeout Pass ]
-
-# Sheriff 2018-08-13
-crbug.com/873829 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-iframe-no-scrolling.html [ Timeout Pass ]
-
 crbug.com/873873 external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
 crbug.com/873873 virtual/outofblink-cors/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
 crbug.com/873873 virtual/outofblink-cors-ns/external/wpt/service-workers/service-worker/fetch-canvas-tainting-video.https.html [ Timeout Pass ]
@@ -5013,9 +4989,57 @@
 # Sheriff 2018-08-20
 crbug.com/862589 [ Linux Mac Win ] virtual/threaded/fast/idle-callback/long_idle_periods.html [ Timeout Pass ]
 
-# Sheriff 2018-08-21
-crbug.com/873829 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-iframe-no-scrolling.html [ Timeout Pass ]
+# fast/events/middleClickAutoscroll-* are failing on Mac
+crbug.com/874162 [ Mac ] fast/events/autoscroll-iframe-no-scrolling.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-iframe-no-scrolling.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-iframe-no-scrolling.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/autoscroll-in-overflow-hidden-html.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-in-overflow-hidden-html.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-in-overflow-hidden-html.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/autoscroll-in-textarea.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-in-textarea.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-in-textarea.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/autoscroll-over-scrollbar.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-over-scrollbar.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-over-scrollbar.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/autoscroll-should-not-stop-on-keypress.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/autoscroll-should-not-stop-on-keypress.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/autoscroll-should-not-stop-on-keypress.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-click-hyperlink.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-click-hyperlink.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-click-hyperlink.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-click.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-click.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-click.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-drag.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-drag.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-drag.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-event-fired.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-event-fired.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-event-fired.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-in-iframe.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-in-iframe.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-in-iframe.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-latching.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-latching.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-latching.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-modal-scrollable-iframe-div.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-nested-divs-forbidden.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-nested-divs-forbidden.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-nested-divs-forbidden.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-nested-divs.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/middleClickAutoscroll-nested-divs.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/middleClickAutoscroll-nested-divs.html [ Skip ]
+crbug.com/874162 [ Mac ] fast/events/selection-autoscroll-borderbelt.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/mouseevent_fractional/fast/events/selection-autoscroll-borderbelt.html [ Skip ]
+crbug.com/874162 [ Mac ] virtual/user-activation-v2/fast/events/selection-autoscroll-borderbelt.html [ Skip ]
 
 # Failures on 32-bit Android when using GPU instead of software rendering
 crbug.com/875172 [ Android ] external/wpt/web-animations/interfaces/Animatable/animate.html [ Pass Failure ]
 crbug.com/875172 [ Android ] http/tests/worklet/webexposed/global-interface-listing-paint-worklet.html [ Pass Failure ]
+
+# Does not set correct realm as scope in current implementation
+crbug.com/872138 external/wpt/client-hints/accept_ch.tentative.https.html [ Timeout Pass ]
+
diff --git a/third_party/WebKit/LayoutTests/compositing/composited-canvas-with-overflowing-object-fit-expected.html b/third_party/WebKit/LayoutTests/compositing/composited-canvas-with-overflowing-object-fit-expected.html
new file mode 100644
index 0000000..8e7122e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/composited-canvas-with-overflowing-object-fit-expected.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<div style="position:absolute; top:30px; padding:10px; border:5px dashed black; width:150px; height:100px; overflow:hidden;">
+  <div style="position:absolute; left:10px; top:10px; width:50px; height:25px; background:black;"></div>
+  <div style="position:absolute; left:110px; top:10px; width:50px; height:25px; background:black;"></div>
+  <div style="position:absolute; left:60px; top:35px; width:50px; height:50px; background:black;"></div>
+  <div style="position:absolute; left:10px; top:85px; width:50px; height:25px; background:black;"></div>
+  <div style="position:absolute; left:110px; top:85px; width:50px; height:25px; background:black;"></div>
+</div>
+<div>This test passes if the overflowing canvas contents are correctly clipped to the content box.</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/composited-canvas-with-overflowing-object-fit.html b/third_party/WebKit/LayoutTests/compositing/composited-canvas-with-overflowing-object-fit.html
new file mode 100644
index 0000000..3a72af6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/composited-canvas-with-overflowing-object-fit.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<canvas width="300" height="300" style="will-change:opacity; position:absolute; top:30px; padding:10px; border:5px dashed black; object-fit:cover; width:150px; height:100px;"></canvas>
+<div>This test passes if the overflowing canvas contents are correctly clipped to the content box.</div>
+<script>
+var context = document.getElementsByTagName("canvas")[0].getContext("2d");
+for (var y = 0; y < 3; y++)
+  for (var x = 0; x < 3; x++)
+    if ((x + y + 1) & 1)
+      context.fillRect(x * 100, y * 100, 100, 100);
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index d44da23..1101935 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -53113,6 +53113,18 @@
      {}
     ]
    ],
+   "css/css-pseudo/first-letter-and-whitespace.html": [
+    [
+     "/css/css-pseudo/first-letter-and-whitespace.html",
+     [
+      [
+       "/css/css-pseudo/first-letter-and-whitespace-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-pseudo/first-letter-block-to-inline.html": [
     [
      "/css/css-pseudo/first-letter-block-to-inline.html",
@@ -74297,18 +74309,6 @@
      {}
     ]
    ],
-   "css/css-values-3/calc-in-counter-001.xhtml": [
-    [
-     "/css/css-values-3/calc-in-counter-001.xhtml",
-     [
-      [
-       "/css/css-values-3/calc-in-counter-001-ref.xhtml",
-       "=="
-      ]
-     ],
-     {}
-    ]
-   ],
    "css/css-values/attr-color-invalid-cast.html": [
     [
      "/css/css-values/attr-color-invalid-cast.html",
@@ -74489,6 +74489,18 @@
      {}
     ]
    ],
+   "css/css-values/calc-in-counter-001.xhtml": [
+    [
+     "/css/css-values/calc-in-counter-001.xhtml",
+     [
+      [
+       "/css/css-values/calc-in-counter-001-ref.xhtml",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-values/calc-in-media-queries-001.html": [
     [
      "/css/css-values/calc-in-media-queries-001.html",
@@ -85517,9 +85529,21 @@
      {}
     ]
    ],
-   "css/motion/offset-path-string.html": [
+   "css/motion/offset-path-string-001.html": [
     [
-     "/css/motion/offset-path-string.html",
+     "/css/motion/offset-path-string-001.html",
+     [
+      [
+       "/css/motion/offset-path-string-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/motion/offset-path-string-002.html": [
+    [
+     "/css/motion/offset-path-string-002.html",
      [
       [
        "/css/motion/offset-path-string-ref.html",
@@ -94829,6 +94853,18 @@
      {}
     ]
    ],
+   "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html": [
+    [
+     "/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html",
+     [
+      [
+       "/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html": [
     [
      "/html/rendering/non-replaced-elements/the-fieldset-element-0/legend-position-relative.html",
@@ -126694,6 +126730,11 @@
      {}
     ]
    ],
+   "css/css-pseudo/first-letter-and-whitespace-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-pseudo/first-letter-block-to-inline-ref.html": [
     [
      {}
@@ -131749,6 +131790,11 @@
      {}
     ]
    ],
+   "css/css-transitions/CSSTransition-startTime.tentative-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-transitions/CSSTransition-transitionProperty.tentative-expected.txt": [
     [
      {}
@@ -133894,11 +133940,6 @@
      {}
     ]
    ],
-   "css/css-values-3/calc-in-counter-001-ref.xhtml": [
-    [
-     {}
-    ]
-   ],
    "css/css-values/META.yml": [
     [
      {}
@@ -133914,6 +133955,11 @@
      {}
     ]
    ],
+   "css/css-values/calc-in-counter-001-ref.xhtml": [
+    [
+     {}
+    ]
+   ],
    "css/css-values/calc-rem-lang-ref.html": [
     [
      {}
@@ -145479,11 +145525,26 @@
      {}
     ]
    ],
+   "fetch/cross-origin-resource-policy/fetch.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "fetch/cross-origin-resource-policy/fetch.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "fetch/cross-origin-resource-policy/fetch.any.worker-expected.txt": [
     [
      {}
     ]
    ],
+   "fetch/cross-origin-resource-policy/fetch.https.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "fetch/cross-origin-resource-policy/iframe-loads-expected.txt": [
     [
      {}
@@ -153669,7 +153730,7 @@
      {}
     ]
    ],
-   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.js": [
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.sharedworker-expected.txt": [
     [
      {}
     ]
@@ -154469,6 +154530,11 @@
      {}
     ]
    ],
+   "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order-ref.html": [
+    [
+     {}
+    ]
+   ],
    "html/rendering/non-replaced-elements/the-fieldset-element-0/legend-expected.txt": [
     [
      {}
@@ -163234,6 +163300,11 @@
      {}
     ]
    ],
+   "payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "payment-request/rejects_if_not_active.https-expected.txt": [
     [
      {}
@@ -168234,6 +168305,11 @@
      {}
     ]
    ],
+   "service-workers/service-worker/navigation-redirect.https-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "service-workers/service-worker/navigation-timing.https-expected.txt": [
     [
      {}
@@ -172824,21 +172900,11 @@
      {}
     ]
    ],
-   "webrtc/RTCConfiguration-bundlePolicy-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "webrtc/RTCConfiguration-helper.js": [
     [
      {}
     ]
    ],
-   "webrtc/RTCConfiguration-iceCandidatePoolSize-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "webrtc/RTCConfiguration-iceServers-expected.txt": [
     [
      {}
@@ -172849,11 +172915,6 @@
      {}
     ]
    ],
-   "webrtc/RTCConfiguration-rtcpMuxPolicy-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "webrtc/RTCDTMFSender-helper.js": [
     [
      {}
@@ -194675,6 +194736,12 @@
      {}
     ]
    ],
+   "css/css-scoping/shadow-multiple-links.html": [
+    [
+     "/css/css-scoping/shadow-multiple-links.html",
+     {}
+    ]
+   ],
    "css/css-scoping/slot-non-html-display-value.html": [
     [
      "/css/css-scoping/slot-non-html-display-value.html",
@@ -214629,20 +214696,22 @@
      {}
     ]
    ],
-   "fetch/cross-origin-resource-policy/fetch-in-service-worker.html": [
-    [
-     "/fetch/cross-origin-resource-policy/fetch-in-service-worker.html",
-     {}
-    ]
-   ],
    "fetch/cross-origin-resource-policy/fetch.any.js": [
     [
      "/fetch/cross-origin-resource-policy/fetch.any.html",
      {}
     ],
     [
+     "/fetch/cross-origin-resource-policy/fetch.any.sharedworker.html",
+     {}
+    ],
+    [
      "/fetch/cross-origin-resource-policy/fetch.any.worker.html",
      {}
+    ],
+    [
+     "/fetch/cross-origin-resource-policy/fetch.https.any.serviceworker.html",
+     {}
     ]
    ],
    "fetch/cross-origin-resource-policy/iframe-loads.html": [
@@ -218165,15 +218234,13 @@
      {}
     ]
    ],
-   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html": [
+   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.js": [
     [
-     "/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html",
+     "/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.sharedworker.html",
      {}
-    ]
-   ],
-   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html": [
+    ],
     [
-     "/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html",
+     "/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.worker.html",
      {}
     ]
    ],
@@ -218507,6 +218574,12 @@
      {}
     ]
    ],
+   "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-block-formatting-context.html": [
+    [
+     "/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-block-formatting-context.html",
+     {}
+    ]
+   ],
    "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-default-style.html": [
     [
      "/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-default-style.html",
@@ -218525,6 +218598,12 @@
      {}
     ]
    ],
+   "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid.html": [
+    [
+     "/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid.html",
+     {}
+    ]
+   ],
    "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-multicol.html": [
     [
      "/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-multicol.html",
@@ -240973,6 +241052,18 @@
      }
     ]
    ],
+   "payment-request/payment-response/onpayerdetailchange-attribute.https.html": [
+    [
+     "/payment-request/payment-response/onpayerdetailchange-attribute.https.html",
+     {}
+    ]
+   ],
+   "payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html": [
+    [
+     "/payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html",
+     {}
+    ]
+   ],
    "payment-request/rejects_if_not_active.https.html": [
     [
      "/payment-request/rejects_if_not_active.https.html",
@@ -279629,7 +279720,7 @@
    "support"
   ],
   "client-hints/accept_ch.tentative.https.html": [
-   "812edca2d3fd438a13bcc05a6c7a722b467e000b",
+   "d4131aaccef13ca64703b37f0bd7f3ccad609f77",
    "testharness"
   ],
   "client-hints/accept_ch.tentative.sub.https.html": [
@@ -321608,6 +321699,14 @@
    "fe8b6b71faa344223eaa4ed781b39699e6f194b0",
    "reftest"
   ],
+  "css/css-pseudo/first-letter-and-whitespace-ref.html": [
+   "69c10648b527ad815d1c8bd2219e061e62e5fb22",
+   "support"
+  ],
+  "css/css-pseudo/first-letter-and-whitespace.html": [
+   "fb88871b852657a0bfdfa02db25020534006e670",
+   "reftest"
+  ],
   "css/css-pseudo/first-letter-block-to-inline-ref.html": [
    "1c8ca71127b09dc729377b71b102bed8095aa249",
    "support"
@@ -321972,6 +322071,10 @@
    "07862ce7d2a954988bdbce882869a4c5f097089a",
    "reftest"
   ],
+  "css/css-scoping/shadow-multiple-links.html": [
+   "d57bae999bf386f430b2fee9e20c802d83ae8588",
+   "testharness"
+  ],
   "css/css-scoping/shadow-reassign-dynamic-001.html": [
    "11ed4da2e6ce88d8a2b98a8f1c814417ef7770dd",
    "reftest"
@@ -334336,8 +334439,12 @@
    "10a2db369b0a95f250d547e8a2bda5707cd55a90",
    "testharness"
   ],
+  "css/css-transitions/CSSTransition-startTime.tentative-expected.txt": [
+   "1246653d1b9ec5d41603d7c59a7d95526dbf5093",
+   "support"
+  ],
   "css/css-transitions/CSSTransition-startTime.tentative.html": [
-   "74a77f7d411224769a79530c310433eb17188054",
+   "ae2afdc82356eda6774f9354fb0b1720fb358f15",
    "testharness"
   ],
   "css/css-transitions/CSSTransition-transitionProperty.tentative-expected.txt": [
@@ -334589,7 +334696,7 @@
    "support"
   ],
   "css/css-transitions/support/helper.js": [
-   "8ee31a15336143dd74c873a1722c380afe289300",
+   "4a1e1f8ddf62a1c12027473cc4d7b70eab594296",
    "support"
   ],
   "css/css-transitions/support/import-green.css": [
@@ -339144,14 +339251,6 @@
    "d3a6c835c23b82a85398e7981461a0cd3a75b861",
    "manual"
   ],
-  "css/css-values-3/calc-in-counter-001-ref.xhtml": [
-   "f9e4ab0f7f9743a5c08b7ac1795df0bae3064484",
-   "support"
-  ],
-  "css/css-values-3/calc-in-counter-001.xhtml": [
-   "9b769e7cbc90f3fb43430c3e3cd679819ff6139b",
-   "reftest"
-  ],
   "css/css-values/META.yml": [
    "05ca3b8ca5cd6ca305a66d552d7ffd5f3185f207",
    "support"
@@ -339236,6 +339335,14 @@
    "6a88138c89c65b513310fb27e1f301ab1d652c98",
    "testharness"
   ],
+  "css/css-values/calc-in-counter-001-ref.xhtml": [
+   "f9e4ab0f7f9743a5c08b7ac1795df0bae3064484",
+   "support"
+  ],
+  "css/css-values/calc-in-counter-001.xhtml": [
+   "9b769e7cbc90f3fb43430c3e3cd679819ff6139b",
+   "reftest"
+  ],
   "css/css-values/calc-in-font-feature-settings.html": [
    "0785a776a398483fab6b535b66210816ce5521af",
    "testharness"
@@ -347428,14 +347535,18 @@
    "3142cc8d33d56a00d40322400d164fbb92701592",
    "reftest"
   ],
+  "css/motion/offset-path-string-001.html": [
+   "f9cdf54e93f1b3646b7a414f4df59a216793cff1",
+   "reftest"
+  ],
+  "css/motion/offset-path-string-002.html": [
+   "8b3f847c13a3e1e74b950f5783173e0608f6d9a6",
+   "reftest"
+  ],
   "css/motion/offset-path-string-ref.html": [
    "fde0c63d10b7f26251a916c2435f1232e7db9b19",
    "support"
   ],
-  "css/motion/offset-path-string.html": [
-   "f9cdf54e93f1b3646b7a414f4df59a216793cff1",
-   "reftest"
-  ],
   "css/motion/offset-rotate-001.html": [
    "d2a73d7472f90b97e39083944b3b046094a125d9",
    "reftest"
@@ -347477,15 +347588,15 @@
    "testharness"
   ],
   "css/motion/parsing/offset-path-parsing-invalid.html": [
-   "eedc48f75690d3915f3fa80d34e62b7e4eac86fa",
+   "167ce2a6a05648c53821040e6f94c592e4b4d9d6",
    "testharness"
   ],
   "css/motion/parsing/offset-path-parsing-valid-expected.txt": [
-   "dd8003dfba27d2c6b4b90621085ac4b9f1001ce5",
+   "adf3ad145a3fe5d8fc743ea9ffe4890e9dc2f06d",
    "support"
   ],
   "css/motion/parsing/offset-path-parsing-valid.html": [
-   "092d2328f325d37ba0ba7b34ee1dc00908a7e356",
+   "87515dc73bbe5b4071ab57099cf914147e63f4a4",
    "testharness"
   ],
   "css/motion/parsing/offset-position-parsing-invalid.html": [
@@ -360200,18 +360311,26 @@
    "7395a63e93fa31ef2b9482fa93ce2b4e92b8c8cb",
    "support"
   ],
-  "fetch/cross-origin-resource-policy/fetch-in-service-worker.html": [
-   "d9db1c57557b20c40909bf91a12f85bfa243b39d",
-   "testharness"
+  "fetch/cross-origin-resource-policy/fetch.any-expected.txt": [
+   "25175c716e6e001e25dc48a247f2409e8f890b0d",
+   "support"
   ],
   "fetch/cross-origin-resource-policy/fetch.any.js": [
-   "497663ca6aa5d76bfead06e176b3f7004e0e7774",
+   "cee8d807a756be883675a3c3b503aa6b738c192c",
    "testharness"
   ],
+  "fetch/cross-origin-resource-policy/fetch.any.sharedworker-expected.txt": [
+   "25175c716e6e001e25dc48a247f2409e8f890b0d",
+   "support"
+  ],
   "fetch/cross-origin-resource-policy/fetch.any.worker-expected.txt": [
    "25175c716e6e001e25dc48a247f2409e8f890b0d",
    "support"
   ],
+  "fetch/cross-origin-resource-policy/fetch.https.any.serviceworker-expected.txt": [
+   "a9b36a511fda86cf2c48f1452a96417ffa4dfaad",
+   "support"
+  ],
   "fetch/cross-origin-resource-policy/iframe-loads-expected.txt": [
    "3d8a5fa9581528e58fa1be86500dfaef59d18cd7",
    "support"
@@ -369964,20 +370083,16 @@
    "594d11e18e7ef8e9aaa0eca3dc2865033827ce7c",
    "testharness"
   ],
-  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html": [
-   "1fa9a3ae8d33bfd08d37a18853d511b9de66889b",
-   "testharness"
-  ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker-expected.txt": [
    "dca7776baefe8e2c5758475769c3e28f265171bb",
    "support"
   ],
-  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html": [
-   "c93bd6d985380cde5e3309897ad56fccd0a73709",
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.js": [
+   "5ba6f2496e2eed2c632ef912a5966ffd44227ac6",
    "testharness"
   ],
-  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.js": [
-   "42b788cb6d8a9509239bbb5c141c1f014dd9a6bf",
+  "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.sharedworker-expected.txt": [
+   "dca7776baefe8e2c5758475769c3e28f265171bb",
    "support"
   ],
   "html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/no-transferring.html": [
@@ -371060,6 +371175,10 @@
    "f5ec83d4ee5ce80ae40a5fe12269d4c7c6c9d91a",
    "support"
   ],
+  "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-block-formatting-context.html": [
+   "7dba7334f7e50a0b6b7e47be9be4a9bd8159c6a5",
+   "testharness"
+  ],
   "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-default-style.html": [
    "8eb13f9a9ea149970510e9d2982905d523125e85",
    "testharness"
@@ -371076,10 +371195,22 @@
    "e917fedffc3d199988f9dc39f13e6660caa3e822",
    "testharness"
   ],
+  "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid.html": [
+   "670ff1c0278b7febb1dea05615e378d5b45d0201",
+   "testharness"
+  ],
   "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-multicol.html": [
    "b769531b2731c8374c77ca8c0d931147a2d9125b",
    "testharness"
   ],
+  "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order-ref.html": [
+   "4512af9943379bcb961c4098501899a00d05feee",
+   "support"
+  ],
+  "html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html": [
+   "ee6d5ca161811bca7826a7489f0e269041c8a5ad",
+   "reftest"
+  ],
   "html/rendering/non-replaced-elements/the-fieldset-element-0/legend-block-formatting-context.html": [
    "80d80d4934f0ad148458c5d5494946f5c7b126c8",
    "testharness"
@@ -393421,13 +393552,25 @@
    "manual"
   ],
   "payment-request/payment-response/helpers.js": [
-   "992e5ce9af876e7d3f76168f15691afd43b41791",
+   "589d50559824d87548c9afd235c9ae364bcbf7b4",
    "support"
   ],
   "payment-request/payment-response/methodName-attribute-manual.https.html": [
    "316eaf783a676265d8833fe9935bf8a7597b8ddb",
    "manual"
   ],
+  "payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt": [
+   "1f103cfef9015d00b36ef74bdaaf492449b8a1bb",
+   "support"
+  ],
+  "payment-request/payment-response/onpayerdetailchange-attribute.https.html": [
+   "7de40114f00a31ef970bf909022ddaf72ec91e59",
+   "testharness"
+  ],
+  "payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html": [
+   "746a85f304d81ae8c3390d6c08096d93d46829c1",
+   "testharness"
+  ],
   "payment-request/payment-response/payerEmail-attribute-manual.https.html": [
    "f21f1e0e996338e6ed51a66ad4ec4ad91fa74883",
    "manual"
@@ -405184,6 +405327,10 @@
    "12f109101a7d6fcb52ab070077297443fa7ab3eb",
    "testharness"
   ],
+  "service-workers/service-worker/navigation-redirect.https-expected.txt": [
+   "3f8ea6985a70441edf6836e2b81bc72f0079c32c",
+   "support"
+  ],
   "service-workers/service-worker/navigation-redirect.https.html": [
    "92638db88808806e49a648c55749f23af4bf8cc1",
    "testharness"
@@ -406861,7 +407008,7 @@
    "testharness"
   ],
   "shadow-dom/Element-interface-attachShadow.html": [
-   "e5b4dedaf8f78a6ce771af4509da8acb3aca3441",
+   "8d5c922046ca2cf788ee737fc7055d99313c8516",
    "testharness"
   ],
   "shadow-dom/Element-interface-shadowRoot-attribute.html": [
@@ -407009,7 +407156,7 @@
    "support"
   ],
   "shadow-dom/resources/shadow-dom-utils.js": [
-   "05a7ed192e44934cb04bc9105d70d3dd8b2a5a28",
+   "40d24bf410273351c3a5a2ee1fa24a7ef2fbaad4",
    "support"
   ],
   "shadow-dom/resources/shadow-dom.js": [
@@ -410729,7 +410876,7 @@
    "testharness"
   ],
   "url/urlsearchparams-constructor.any.js": [
-   "b73cb13af7d312177c9ae8219343a1fc42e80738",
+   "6ccb13f270880b300d5079bb1528bad4b6c8ab49",
    "testharness"
   ],
   "url/urlsearchparams-delete.any.js": [
@@ -411705,11 +411852,11 @@
    "testharness"
   ],
   "web-animations/timing-model/animations/playing-an-animation-expected.txt": [
-   "55eb53101355620f9331bc022a962afb627f30c6",
+   "74b76641c5dc0e6b2e10d91da98efa0c91a41362",
    "support"
   ],
   "web-animations/timing-model/animations/playing-an-animation.html": [
-   "1ef3259d06115d748ee92c143bdf07d9ce665224",
+   "e647d98e7439c8c84efe9610d974bde55d49b14c",
    "testharness"
   ],
   "web-animations/timing-model/animations/reversing-an-animation-expected.txt": [
@@ -413752,10 +413899,6 @@
    "cc1191d16707c4a5126291850414800cacb76199",
    "testharness"
   ],
-  "webrtc/RTCConfiguration-bundlePolicy-expected.txt": [
-   "eb90509996cf5ef3b51ac698f80d7585712bc8e3",
-   "support"
-  ],
   "webrtc/RTCConfiguration-bundlePolicy.html": [
    "2ae7780f86084baf6478bf92a8440cf142fe8611",
    "testharness"
@@ -413764,16 +413907,12 @@
    "eb27b6312d0a3e6f0c6af7d366fc1172aeacea99",
    "support"
   ],
-  "webrtc/RTCConfiguration-iceCandidatePoolSize-expected.txt": [
-   "61a7ffbb058a16327cd5252b39b3b8c3f237cce6",
-   "support"
-  ],
   "webrtc/RTCConfiguration-iceCandidatePoolSize.html": [
    "6b55bd01a1422f2315cae765a7d539bf81151c1f",
    "testharness"
   ],
   "webrtc/RTCConfiguration-iceServers-expected.txt": [
-   "aab3846b69c88839c9ec9ad3faa59ff3454e64d6",
+   "c9fdd547646af7f1129e81be08419ac629a9fce7",
    "support"
   ],
   "webrtc/RTCConfiguration-iceServers.html": [
@@ -413781,17 +413920,13 @@
    "testharness"
   ],
   "webrtc/RTCConfiguration-iceTransportPolicy-expected.txt": [
-   "ff509a031e950c415fdadfc5b378fef67c2054db",
+   "2f989c24373c933bcc9397e93486f921a868f136",
    "support"
   ],
   "webrtc/RTCConfiguration-iceTransportPolicy.html": [
    "a3cdf93a5c3da4552c616f43e1fca01b9261dd38",
    "testharness"
   ],
-  "webrtc/RTCConfiguration-rtcpMuxPolicy-expected.txt": [
-   "5305921e0659d9ca5e6bcce8da5a974a40575fb9",
-   "support"
-  ],
   "webrtc/RTCConfiguration-rtcpMuxPolicy.html": [
    "98a64f101f3cea18fec9d5585321e742ded4b13b",
    "testharness"
@@ -414385,7 +414520,7 @@
    "support"
   ],
   "webrtc/idlharness.https.window-expected.txt": [
-   "f093e9bc8d6b37ec6d36a928145ac5822d279984",
+   "167fcf23339122dcf75efc3804727422c8db5ec5",
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/client-hints/accept_ch.tentative.https.html b/third_party/WebKit/LayoutTests/external/wpt/client-hints/accept_ch.tentative.https.html
index b8379de..491ef3c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/client-hints/accept_ch.tentative.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/client-hints/accept_ch.tentative.https.html
@@ -52,7 +52,7 @@
 function acceptChLoaded() {
   // Open a new window. Verify that the user agent does not attach the client
   // hints.
-  var verify_win = window.open("do_not_expect_client_hints_headers.html");
+  var verify_win = window.open("resources/do_not_expect_client_hints_headers.html");
   assert_not_equals(verify_win, null, "Popup windows not allowed?");
 }
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping/shadow-multiple-links.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping/shadow-multiple-links.html
new file mode 100644
index 0000000..d1c4fd9eb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping/shadow-multiple-links.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>CSS Test: ShadowRoot with multiple sheet links with the same href shouldn't crash</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
+<div id="host"></div>
+<script>
+test(function() {
+  host.attachShadow({ mode: "open" }).innerHTML = `
+    <link rel="stylesheet" href="data:text/css,">
+    <link rel="stylesheet" href="data:text/css,">
+  `;
+}, "Multiple stylesheets with the same href in a ShadowRoot should not assert or crash");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
new file mode 100644
index 0000000..d90daa453
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS The start time of a newly-created transition is unresolved
+PASS The start time of transitions is based on when they are generated
+PASS The start time of a transition can be set
+PASS The start time can be set to seek a transition
+FAIL Seeking a transition using start time dispatches transition events promise_test: Unhandled rejection with value: object "Error: Timed out waiting for Promise to resolve: transitionstart"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html
index 7c74ed20..b6482f8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html
@@ -107,10 +107,18 @@
   const timelineTime = animation.timeline.currentTime;
 
   animation.startTime = timelineTime - 100 * MS_PER_SEC;
-  await eventWatcher.wait_for('transitionstart');
+  await frameTimeout(
+    eventWatcher.wait_for('transitionstart'),
+    2,
+    'transitionstart'
+  );
 
   animation.startTime = timelineTime - 200 * MS_PER_SEC;
-  await eventWatcher.wait_for('transitionend');
+  await frameTimeout(
+    eventWatcher.wait_for('transitionend'),
+    2,
+    'transitionend'
+  );
 }, 'Seeking a transition using start time dispatches transition events');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/support/helper.js b/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/support/helper.js
index 2e7ecff2..aa3efe8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/support/helper.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-transitions/support/helper.js
@@ -275,4 +275,45 @@
 root.waitForAllAnimations = animations =>
   Promise.all(animations.map(animation => animation.ready));
 
+/**
+ * Utility that takes a Promise and a maximum number of frames to wait and
+ * returns a new Promise that behaves as follows:
+ *
+ * - If the provided Promise resolves _before_ the specified number of frames
+ *   have passed, resolves with the result of the provided Promise.
+ * - If the provided Promise rejects _before_ the specified number of frames
+ *   have passed, rejects with the error result of the provided Promise.
+ * - Otherwise, rejects with a 'Timed out' error message. If |message| is
+ *   provided, it will be appended to the error message.
+ */
+root.frameTimeout = (promiseToWaitOn, framesToWait, message) => {
+  let framesRemaining = framesToWait;
+  let aborted = false;
+
+  const timeoutPromise = new Promise(function waitAFrame(resolve, reject) {
+    if (aborted) {
+      resolve();
+      return;
+    }
+    if (framesRemaining-- > 0) {
+      requestAnimationFrame(() => {
+        waitAFrame(resolve, reject);
+      });
+      return;
+    }
+    let errorMessage = 'Timed out waiting for Promise to resolve';
+    if (message) {
+      errorMessage += `: ${message}`;
+    }
+    reject(new Error(errorMessage));
+  });
+
+  const wrappedPromiseToWaitOn = promiseToWaitOn.then(result => {
+    aborted = true;
+    return result;
+  });
+
+  return Promise.race([timeoutPromise, wrappedPromiseToWaitOn]);
+};
+
 })(window);
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-values-3/calc-in-counter-001-ref.xhtml b/third_party/WebKit/LayoutTests/external/wpt/css/css-values/calc-in-counter-001-ref.xhtml
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/css/css-values-3/calc-in-counter-001-ref.xhtml
rename to third_party/WebKit/LayoutTests/external/wpt/css/css-values/calc-in-counter-001-ref.xhtml
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-values-3/calc-in-counter-001.xhtml b/third_party/WebKit/LayoutTests/external/wpt/css/css-values/calc-in-counter-001.xhtml
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/css/css-values-3/calc-in-counter-001.xhtml
rename to third_party/WebKit/LayoutTests/external/wpt/css/css-values/calc-in-counter-001.xhtml
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string-001.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string.html
rename to third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string-001.html
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string-002.html
new file mode 100644
index 0000000..0d2fcbb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-string-002.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>CSS Motion Path: path(string) paths</title>
+    <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-path-property">
+    <link rel="match" href="offset-path-string-ref.html">
+    <meta name="assert" content="This tests that path(<string>) generates a rotation and translation.">
+    <style>
+      #target {
+        position: absolute;
+        left: 300px;
+        top: 0px;
+        width: 300px;
+        height: 200px;
+        background-color: lime;
+        transform-origin: 0px 0px;
+        offset-path: path('m 0 120 v 200');
+      }
+    </style>
+  </head>
+  <body>
+    <div id="target"></div>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-invalid.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-invalid.html
index d941018..67c1099 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-invalid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-invalid.html
@@ -14,7 +14,7 @@
 <script>
 // arc path segments must have at least 7 arguments.
 // https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
-test_invalid_value("offset-path", "path('M 20 30 A 60 70 80')");
+test_invalid_value("offset-path", 'path("M 20 30 A 60 70 80")');
 
 test_invalid_value("offset-path", "ray(0 sides)");
 test_invalid_value("offset-path", "ray(0deg)");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt
index bfae70a..c7c92964 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt
@@ -6,8 +6,8 @@
 PASS e.style['offset-path'] = "ray(270deg farthest-corner contain)" should set the property value
 PASS e.style['offset-path'] = "ray(-720deg sides)" should set the property value
 PASS e.style['offset-path'] = "ray(calc(180deg - 45deg) farthest-side)" should set the property value
-PASS e.style['offset-path'] = "path('m 0 0 h -100')" should set the property value
-PASS e.style['offset-path'] = "path('M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z')" should set the property value
+FAIL e.style['offset-path'] = "path(\"m 0 0 h -100\")" should set the property value assert_equals: serialization should be canonical expected "path(\"m 0 0 h -100\")" but got "path('m 0 0 h -100')"
+FAIL e.style['offset-path'] = "path(\"M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z\")" should set the property value assert_equals: serialization should be canonical expected "path(\"M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z\")" but got "path('M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z')"
 FAIL e.style['offset-path'] = "url(\"http://www.example.com/index.html#polyline1\")" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['offset-path'] = "circle(100px)" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['offset-path'] = "margin-box" should set the property value assert_not_equals: property should be set got disallowed value ""
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html
index 44867326..d57b465 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html
@@ -21,8 +21,8 @@
 test_valid_value("offset-path", "ray(-720deg sides)");
 test_valid_value("offset-path", "ray(calc(180deg - 45deg) farthest-side)", "ray(calc(135deg) farthest-side)");
 
-test_valid_value("offset-path", "path('m 0 0 h -100')");
-test_valid_value("offset-path", "path('M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z')");
+test_valid_value("offset-path", 'path("m 0 0 h -100")');
+test_valid_value("offset-path", 'path("M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z")');
 
 test_valid_value("offset-path", 'url("http://www.example.com/index.html#polyline1")');
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch-in-service-worker.html b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch-in-service-worker.html
deleted file mode 100644
index e493904e..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch-in-service-worker.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<title>Cross-Origin-Resource-Policy in Service Worker</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
-<script>
-
-service_worker_test(
-    'fetch.any.js',
-    'fetch.any.js test');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any-expected.txt
new file mode 100644
index 0000000..f5d309fa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.
+PASS Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.
+PASS Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.
+PASS Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+PASS Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.
+FAIL Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection. assert_unreached: Should have rejected: undefined Reached unreachable code
+PASS Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a cross-origin redirection.
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.js b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.js
index 758c52a6..ded3bdc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.js
@@ -1,10 +1,5 @@
+// META: global=window,worker
 // META: script=/common/get-host-info.sub.js
-// META: script=/resources/testharness.js
-
-if (!self.document) {
-    importScripts("/resources/testharness.js");
-    importScripts("/common/get-host-info.sub.js");
-}
 
 const host = get_host_info();
 const path = "/fetch/cross-origin-resource-policy/";
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.sharedworker-expected.txt
new file mode 100644
index 0000000..f5d309fa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.any.sharedworker-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.
+PASS Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.
+PASS Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.
+PASS Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+PASS Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.
+FAIL Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection. assert_unreached: Should have rejected: undefined Reached unreachable code
+PASS Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a cross-origin redirection.
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.https.any.serviceworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.https.any.serviceworker-expected.txt
new file mode 100644
index 0000000..1db488e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/fetch/cross-origin-resource-policy/fetch.https.any.serviceworker-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.
+PASS Same-origin fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.
+FAIL Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
+FAIL Cross-origin cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
+PASS Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header.
+PASS Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header.
+PASS Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-site' response header.
+FAIL Cross-origin no-cors fetch to a same-site URL with a 'Cross-Origin-Resource-Policy: same-origin' response header. assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Valid cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-site' response header. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
+PASS Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a redirection.
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' response header after a cross-origin redirection. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
+FAIL Cross-origin no-cors fetch with a 'Cross-Origin-Resource-Policy: same-origin' redirect response header. promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html b/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html
deleted file mode 100644
index de097b2..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-dedicatedworker.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Structured cloning of SharedArrayBuffers into a dedicated worker nested inside a dedicated worker</title>
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
-<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<div id="log"></div>
-
-<script>
-"use strict";
-fetch_tests_from_worker(new Worker("nested-worker-success.js"));
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html b/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html
deleted file mode 100644
index cf328be..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success-sharedworker.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Structured cloning of SharedArrayBuffers into a dedicated worker nested inside a shared worker</title>
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#structuredserialize">
-<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<div id="log"></div>
-
-<script>
-"use strict";
-fetch_tests_from_worker(new SharedWorker("nested-worker-success.js"));
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.js b/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.js
similarity index 73%
rename from third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.js
rename to third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.js
index e1a2843..5388ebcc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.js
@@ -1,11 +1,9 @@
+// META: global=!default,dedicatedworker,sharedworker
+// META: script=resources/test-incrementer.js
 "use strict";
-importScripts("/resources/testharness.js");
-importScripts("resources/test-incrementer.js");
 
 promise_test(t => {
   const worker = new Worker("resources/incrementer-worker.js");
 
   return testSharingViaIncrementerScript(t, worker, "parent worker", worker, "sub-worker");
 }, "postMessaging to a dedicated sub-worker allows them to see each others' modifications");
-
-done();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.sharedworker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.sharedworker-expected.txt
new file mode 100644
index 0000000..7b70ea2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/nested-worker-success.any.sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL postMessaging to a dedicated sub-worker allows them to see each others' modifications Worker is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-block-formatting-context.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-block-formatting-context.html
new file mode 100644
index 0000000..c38944a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-block-formatting-context.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>The fieldset element: block formatting context</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+* {
+  margin: 0;
+  padding: 0;
+}
+fieldset { border: none; }
+.float {
+  float: left;
+  width: 50px;
+  height: 50px;
+  background-color: orange;
+}
+</style>
+
+<div class=float></div>
+<fieldset><div class=float></div></fieldset>
+
+<script>
+test(() => {
+  const fieldset = document.querySelector('fieldset');
+  assert_equals(fieldset.offsetTop, 0, 'fieldset.offsetTop');
+  assert_equals(fieldset.offsetLeft, 50, 'fieldset.offsetLeft');
+  assert_equals(fieldset.clientHeight, 50, 'fieldset.clientHeight');
+});
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid.html
new file mode 100644
index 0000000..7e9cf36
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<title>fieldset and CSS Grid</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#test, #ref, #test-inline, #ref-inline {
+  display: grid;
+  grid-template-columns: auto 50px auto;
+  grid-template-rows: auto 50px auto;
+  margin: 0;
+  padding: 0;
+  border: none
+}
+#test-inline, #ref-inline { display: inline-grid }
+</style>
+<fieldset id=test>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</fieldset>
+<hr>
+<div id=ref>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</div>
+<hr>
+<fieldset id=test-inline>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</fieldset>
+<div id=ref-inline>
+ <div>1</div>
+ <div>2</div>
+ <div>3</div>
+ <div>4</div>
+ <div>5</div>
+ <div>6</div>
+ <div>7</div>
+ <div>8</div>
+ <div>9</div>
+</div>
+<script>
+  test(() => {
+    assert_equals(getComputedStyle(document.getElementById('test')).height,
+                  getComputedStyle(document.getElementById('ref')).height);
+  }, "Grid");
+
+  test(() => {
+    assert_equals(getComputedStyle(document.getElementById('test-inline')).height,
+                  getComputedStyle(document.getElementById('ref-inline')).height);
+  }, "Inline grid");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order-ref.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order-ref.html
new file mode 100644
index 0000000..13b262a8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order-ref.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Reference for fieldset painting order</title>
+<style>
+div { width: 200px; height: 200px; }
+#a { background: green; }
+#b { background: lime; position: relative; top: -100px; left: 100px; }
+</style>
+<p>There should be no red.</p>
+<div id=a></div>
+<div id=b></div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html
new file mode 100644
index 0000000..7bd2ced
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-painting-order.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>fieldset painting order</title>
+<link rel=match href=fieldset-painting-order-ref.html>
+<style>
+fieldset, legend { margin: 0; padding: 0; }
+fieldset {
+  border: 100px solid red;
+  width: 0;
+  min-width: 0;
+  height: 0;
+}
+legend { width: 200px; height: 200px; margin-left: -100px; background: green; }
+legend > span { float: right; margin-top: 100px; width: 100px; height: 100px; background: red; }
+fieldset > div { margin-top: -100px; background: lime; width: 200px; height: 200px; }
+</style>
+<p>There should be no red.</p>
+<fieldset><legend><span></span></legend><div></div></fieldset>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/helpers.js b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/helpers.js
index bf938c5e..807f794e 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/helpers.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/helpers.js
@@ -35,7 +35,7 @@
  * @param PaymentOptions options
  */
 async function getPaymentResponse(options, id) {
-  const { response } = getPaymentRequestResponse(options, id);
+  const { response } = await getPaymentRequestResponse(options, id);
   return response;
 }
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt
new file mode 100644
index 0000000..69cb5dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL PaymentResponse inherits from EventTarget assert_equals: expected function "function EventTarget() { [native code] }" but got function "function () { [native code] }"
+FAIL PaymentResponse has an onpayerdetailchange in the prototype chain assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https.html
new file mode 100644
index 0000000..ed9e6e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.https.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>PaymentResponse.prototype.onpayerdetailschange attribute</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(() => {
+  assert_equals(Object.getPrototypeOf(PaymentResponse), window.EventTarget);
+}, "PaymentResponse inherits from EventTarget");
+
+test(() => {
+  assert_true("onpayerdetailchange" in PaymentResponse.prototype);
+}, "PaymentResponse has an onpayerdetailchange in the prototype chain");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html
new file mode 100644
index 0000000..9f92d28
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/payment-request/payment-response/onpayerdetailchange-attribute.manual.https.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>PaymentResponse.prototype.onpayerdetailchange attribute</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="helpers.js"></script>
+<script>
+function runTest(button, options, expected){
+  button.disabled = true;
+  promise_test(async () => {
+    const response = await getPaymentResponse(options);
+    const eventPromise = new Promise(resolve => {
+      response.addEventListener("payerdetailchange", resolve);
+    });
+    const error = button.previousElementSibling.textContent.trim();
+    const retryPromise = response.retry({ error });
+    const event = await eventPromise;
+    assert_true(event instanceof PaymentRequestUpdateEvent);
+    for(const [prop, value] of Object.entries(expected)){
+      assert_equals(response[prop], value);
+    }
+    await response.complete("success");
+  }, button.textContent.trim());
+}
+</script>
+<h2>Handling PaymentResponse.prototype.onpayerdetailchange events</h2>
+<p>
+  Each button will bring up the Payment Request UI window.
+  When shown the payment sheet, use any details and hit pay.
+</p>
+<p>
+  When asked to retry the payment:
+</p>
+<ol>
+  <li>
+    <p>
+      Change payer's name to "pass".
+    </p>
+    <button onclick="runTest(this, { requestPayerName: true }, { payerName: 'pass' });">
+      PaymentRequestUpdateEvent is dispatched when payer name changes.
+    </button>
+  </li>
+  <li>
+    <p>
+      Change payer's email to "pass@pass.pass".
+    </p>
+    <button onclick="runTest(this, {requestPayerEmail: true}, { payerEmail: 'pass@pass.pass' });">
+      PaymentRequestUpdateEvent is dispatched when payer email changes.
+    </button>
+  </li>
+  <li>
+    <p>
+      Change payer's phone to "+1-800-000-0000".
+    </p>
+    <button onclick="runTest(this, {requestPayerPhone: true}, { payerPhone: '+1-800-000-0000' })">
+      PaymentRequestUpdateEvent is dispatched when payer phone changes.
+    </button>
+  </li>
+  <li>
+    <button onclick="done();">DONE!</button>
+  </li>
+</ol>
+<small>
+  If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+  and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/OWNERS">owners</a>.
+</small>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webxr-test.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webxr-test.js
index 10ed703..e5d17259 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webxr-test.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webxr-test.js
@@ -9,7 +9,11 @@
   }
 
   simulateDeviceConnection(init_params) {
-    return Promise.resolve(this.mockVRService_.addDevice(init_params));
+    return Promise.resolve(this.mockVRService_.addRuntime(init_params));
+  }
+
+  simulateDeviceDisconnection(device) {
+    this.mockVRService_.removeRuntime(device);
   }
 
   simulateUserActivation(callback) {
@@ -30,10 +34,12 @@
 }
 
 // Mocking class definitions
+
+// Mock service implements both the VRService and XRDevice mojo interfaces.
 class MockVRService {
   constructor() {
     this.bindingSet_ = new mojo.BindingSet(device.mojom.VRService);
-    this.devices_ = [];
+    this.runtimes_ = [];
 
     this.interceptor_ =
         new MojoInterfaceInterceptor(device.mojom.VRService.name);
@@ -43,31 +49,87 @@
   }
 
   // Test methods
-  addDevice(fakeDeviceInit) {
-    let device = new MockDevice(fakeDeviceInit, this);
-    this.devices_.push(device);
+  addRuntime(fakeDeviceInit) {
+    let runtime = new MockRuntime(fakeDeviceInit, this);
+    this.runtimes_.push(runtime);
 
     if (this.client_) {
       this.client_.onDeviceChanged();
     }
 
-    return device;
+    return runtime;
+  }
+
+  removeRuntime(runtime) {
+    // We have no way of distinguishing between devices, so just clear the
+    // entire list for now.
+    // TODO(http://crbug.com/873409) We also have no way right now to disconnect
+    // devices.
+    this.runtimes_ = [];
   }
 
   // VRService implementation.
   requestDevice() {
-    return Promise.resolve(
-        {device: this.devices_[0] ? this.devices_[0].getDevicePtr() : null});
+    if (this.runtimes_.length > 0) {
+      let devicePtr = new device.mojom.XRDevicePtr();
+      new mojo.Binding(
+          device.mojom.XRDevice, this, mojo.makeRequest(devicePtr));
+
+      return Promise.resolve({device: devicePtr});
+    } else {
+      return Promise.resolve({device: null});
+    }
   }
 
   setClient(client) {
     this.client_ = client;
   }
+
+  // XRDevice implementation.
+  requestSession(sessionOptions, was_activation) {
+    let requests = [];
+    // Request a session from all the runtimes.
+    for (let i = 0; i < this.runtimes_.length; i++) {
+      requests[i] = this.runtimes_[i].requestRuntimeSession(sessionOptions);
+    }
+
+    return Promise.all(requests).then((results) => {
+      // Find and return the first successful result.
+      for (let i = 0; i < results.length; i++) {
+        if (results[i].session) {
+          return results[i];
+        }
+      }
+
+      // If there were no successful results, returns a null session.
+      return {session: null};
+    });
+  }
+
+  supportsSession(sessionOptions) {
+    let requests = [];
+    // Check supports on all the runtimes.
+    for (let i = 0; i < this.runtimes_.length; i++) {
+      requests[i] = this.runtimes_[i].runtimeSupportsSession(sessionOptions);
+    }
+
+    return Promise.all(requests).then((results) => {
+      // Find and return the first successful result.
+      for (let i = 0; i < results.length; i++) {
+        if (results[i].supportsSession) {
+          return results[i];
+        }
+      }
+
+      // If there were no successful results, returns false.
+      return {supportsSession: false};
+    });
+  };
 }
 
-// Implements both XRDevice and VRMagicWindowProvider. Maintains a mock for
-// XRPresentationProvider.
-class MockDevice {
+// Implements XRFrameDataProvider and XRPresentationProvider. Maintains a mock
+// for XRPresentationProvider.
+class MockRuntime {
   constructor(fakeDeviceInit, service) {
     this.sessionClient_ = new device.mojom.XRSessionClientPtr();
     this.presentation_provider_ = new MockXRPresentationProvider();
@@ -86,13 +148,6 @@
     }
   }
 
-  // Functions for setup.
-  getDevicePtr() {
-    let devicePtr = new device.mojom.XRDevicePtr();
-    new mojo.Binding(device.mojom.XRDevice, this, mojo.makeRequest(devicePtr));
-    return devicePtr;
-  }
-
   // Test methods.
   setXRPresentationFrameData(poseMatrix, views) {
     if (poseMatrix == null) {
@@ -273,9 +328,10 @@
     // do not have any use for this data at present.
   }
 
-  // XRDevice implementation.
-  requestSession(sessionOptions, was_activation) {
-    return this.supportsSession(sessionOptions).then((result) => {
+
+  // Utility function
+  requestRuntimeSession(sessionOptions) {
+    return this.runtimeSupportsSession(sessionOptions).then((result) => {
       // The JavaScript bindings convert c_style_names to camelCase names.
       let options = new device.mojom.XRPresentationTransportOptions();
       options.transportMethod =
@@ -320,7 +376,7 @@
     });
   }
 
-  supportsSession(options) {
+  runtimeSupportsSession(options) {
     return Promise.resolve({
       supportsSession:
           !options.immersive || this.displayInfo_.capabilities.canPresent
diff --git a/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow.html b/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow.html
index 6b07f7a..fbf20e8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/Element-interface-attachShadow.html
@@ -7,6 +7,7 @@
 <link rel="help" href="https://w3c.github.io/webcomponents/spec/shadow/#widl-Element-attachShadow-ShadowRoot-ShadowRootInit-shadowRootInitDict">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="resources/shadow-dom-utils.js"></script>
 </head>
 <body>
 <div id="log"></div>
@@ -78,7 +79,7 @@
 }, 'Element.attachShadow must throw a InvalidStateError if the context object already hosts a shadow tree');
 
 test(function () {
-    for (var elementName of ['button', 'details', 'input', 'marquee', 'meter', 'progress', 'select', 'textarea', 'keygen']) {
+    for (var elementName of ATTACHSHADOW_DISALLOWED_ELEMENTS) {
         assert_throws({'name': 'NotSupportedError'}, function () {
             document.createElement(elementName).attachShadow({mode: "open"});
         }, 'Calling attachShadow({mode: "open"}) on ' + elementName + ' element must throw');
@@ -87,7 +88,7 @@
             document.createElement(elementName).attachShadow({mode: "closed"});
         }, 'Calling attachShadow({mode: "closed"}) on ' + elementName + ' element must throw');
     }
-}, 'Element.attachShadow must throw a NotSupportedError for button, details, input, marquee, meter, progress, select, textarea, and keygen elements');
+}, 'Element.attachShadow must throw a NotSupportedError for non-safelisted elements');
 
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/resources/shadow-dom-utils.js b/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/resources/shadow-dom-utils.js
index 07db343..37c9a9a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/resources/shadow-dom-utils.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/shadow-dom/resources/shadow-dom-utils.js
@@ -59,6 +59,8 @@
     'wbr'
 ];
 
+var ATTACHSHADOW_DISALLOWED_ELEMENTS = HTML5_ELEMENT_NAMES.filter(el => !ATTACHSHADOW_SAFELISTED_ELEMENTS.includes(el));
+
 function unit(f) {
     return function () {
         var ctx = newContext();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/url/urlsearchparams-constructor.any.js b/third_party/WebKit/LayoutTests/external/wpt/url/urlsearchparams-constructor.any.js
index 5412fc8..6fff03f0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/url/urlsearchparams-constructor.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/url/urlsearchparams-constructor.any.js
@@ -15,20 +15,20 @@
 }, "URLSearchParams constructor, no arguments")
 
 test(() => {
-    params = new URLSearchParams(DOMException);
+    var params = new URLSearchParams(DOMException);
     assert_equals(params.toString(), "INDEX_SIZE_ERR=1&DOMSTRING_SIZE_ERR=2&HIERARCHY_REQUEST_ERR=3&WRONG_DOCUMENT_ERR=4&INVALID_CHARACTER_ERR=5&NO_DATA_ALLOWED_ERR=6&NO_MODIFICATION_ALLOWED_ERR=7&NOT_FOUND_ERR=8&NOT_SUPPORTED_ERR=9&INUSE_ATTRIBUTE_ERR=10&INVALID_STATE_ERR=11&SYNTAX_ERR=12&INVALID_MODIFICATION_ERR=13&NAMESPACE_ERR=14&INVALID_ACCESS_ERR=15&VALIDATION_ERR=16&TYPE_MISMATCH_ERR=17&SECURITY_ERR=18&NETWORK_ERR=19&ABORT_ERR=20&URL_MISMATCH_ERR=21&QUOTA_EXCEEDED_ERR=22&TIMEOUT_ERR=23&INVALID_NODE_TYPE_ERR=24&DATA_CLONE_ERR=25")
     assert_throws(new TypeError(), () => new URLSearchParams(DOMException.prototype),
                   "Constructing a URLSearchParams from DOMException.prototype should throw due to branding checks");
 }, "URLSearchParams constructor, DOMException as argument")
 
 test(() => {
-    params = new URLSearchParams('');
+    var params = new URLSearchParams('');
     assert_true(params != null, 'constructor returned non-null value.');
     assert_equals(params.__proto__, URLSearchParams.prototype, 'expected URLSearchParams.prototype as prototype.');
 }, "URLSearchParams constructor, empty string as argument")
 
 test(() => {
-    params = new URLSearchParams({});
+    var params = new URLSearchParams({});
     assert_equals(params + '', "");
 }, 'URLSearchParams constructor, {} as argument');
 
@@ -37,11 +37,13 @@
     assert_true(params != null, 'constructor returned non-null value.');
     assert_true(params.has('a'), 'Search params object has name "a"');
     assert_false(params.has('b'), 'Search params object has not got name "b"');
-    var params = new URLSearchParams('a=b&c');
+
+    params = new URLSearchParams('a=b&c');
     assert_true(params != null, 'constructor returned non-null value.');
     assert_true(params.has('a'), 'Search params object has name "a"');
     assert_true(params.has('c'), 'Search params object has name "c"');
-    var params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8');
+
+    params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8');
     assert_true(params != null, 'constructor returned non-null value.');
     assert_true(params.has('a'), 'Search params object has name "a"');
     assert_true(params.has('a b'), 'Search params object has name "a b"');
@@ -167,7 +169,7 @@
 })
 
 test(() => {
-  params = new URLSearchParams()
+  var params = new URLSearchParams()
   params[Symbol.iterator] = function *() {
     yield ["a", "b"]
   }
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation-expected.txt
index 46effd3..e7e4e82 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation-expected.txt
@@ -6,6 +6,7 @@
 PASS Playing a finished animation clears the start time
 PASS The ready promise should be replaced if the animation is not already pending
 PASS A pending ready promise should be resolved and not replaced when the animation enters the running state
+PASS Resuming an animation from paused calculates start time from hold time
 FAIL If a pause operation is interrupted, the ready promise is reused assert_true: Animation is pending expected true got undefined
 FAIL A pending playback rate is used when determining auto-rewind behavior promise_test: Unhandled rejection with value: object "TypeError: animation.updatePlaybackRate is not a function"
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation.html b/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation.html
index 80b09e1..1477261 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/animations/playing-an-animation.html
@@ -82,6 +82,25 @@
 
 promise_test(async t => {
   const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+  animation.currentTime = 50 * MS_PER_SEC;
+  await animation.ready;
+
+  animation.pause();
+  await animation.ready;
+
+  const holdTime = animation.currentTime;
+
+  animation.play();
+  await animation.ready;
+
+  assert_less_than_equal(
+    animation.startTime,
+    animation.timeline.currentTime - holdTime + TIME_PRECISION
+  );
+}, 'Resuming an animation from paused calculates start time from hold time');
+
+promise_test(async t => {
+  const animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
   await animation.ready;
 
   // Go to pause-pending state
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/navigator_xr_requestDevice.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/navigator_xr_requestDevice.https.html
new file mode 100644
index 0000000..c51dd8db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/navigator_xr_requestDevice.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+
+  <script>
+    xr_promise_test("navigator.xr.requestDevice returns a device", (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive: true })
+        .then( (controller) => { return navigator.xr.requestDevice() })
+        .then( (device) => {
+          t.step(() => {
+            assert_true(device != null);
+            assert_true(device instanceof XRDevice);
+          });
+        });
+    });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/navigator_xr_requestDevice_no_device.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/navigator_xr_requestDevice_no_device.https.html
new file mode 100644
index 0000000..3cd149b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/navigator_xr_requestDevice_no_device.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+
+  <script>
+    promise_test( (t) => {
+      return promise_rejects(t, 'NotFoundError', navigator.xr.requestDevice());
+    }, "navigator.xr.requestDevice properly rejects when there are 0 devices");
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/resources/webxr_util.js b/third_party/WebKit/LayoutTests/external/wpt/webxr/resources/webxr_util.js
index eaa7887..d73cf8d 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webxr/resources/webxr_util.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/resources/webxr_util.js
@@ -7,7 +7,7 @@
 //
 //   --enable-blink-features=MojoJS,MojoJSTest
 
-function xr_promise_test(func, name, properties) {
+function xr_promise_test(name, func, properties) {
   promise_test(async (t) => {
     // Perform any required test setup:
 
@@ -20,6 +20,74 @@
   }, name, properties);
 }
 
+// A test function which runs through the common steps of requesting a session.
+// Calls the passed in test function with the session, the controller for the
+// device, and the test object.
+// Requires a webglCanvas on the page.
+function xr_session_promise_test(
+    name, func, fakeDeviceInit, sessionOptions, properties) {
+  let testDevice;
+  let testDeviceController;
+  let testSession;
+
+  const webglCanvas = document.getElementsByTagName('canvas')[0];
+  if (!webglCanvas) {
+    promise_test(async (t) => {
+      Promise.reject('xr_session_promise_test requires a canvas on the page!');
+    }, name, properties);
+  }
+  let gl = webglCanvas.getContext('webgl', {alpha: false, antialias: false});
+
+  xr_promise_test(
+      name,
+      (t) =>
+          XRTest.simulateDeviceConnection(fakeDeviceInit)
+              .then((controller) => {
+                testDeviceController = controller;
+                return navigator.xr.requestDevice();
+              })
+              .then((device) => {
+                testDevice = device;
+                return gl.setCompatibleXRDevice(device);
+              })
+              .then(() => new Promise((resolve, reject) => {
+                      // Perform the session request in a user gesture.
+                      XRTest.simulateUserActivation(() => {
+                        testDevice.requestSession(sessionOptions)
+                            .then((session) => {
+                              testSession = session;
+                              // Session must have a baseLayer or frame requests
+                              // will be ignored.
+                              session.baseLayer = new XRWebGLLayer(session, gl);
+                              resolve(func(session, testDeviceController, t));
+                            })
+                            .catch((err) => {
+                              reject(
+                                  'Session with params ' +
+                                  JSON.stringify(sessionOptions) +
+                                  ' was rejected on device ' +
+                                  JSON.stringify(fakeDeviceInit) +
+                                  ' with error: ' + err);
+                            });
+                      });
+                    }))
+              .then(() => {
+                // Cleanup system state.
+                testSession.end().catch(() => {});
+                XRTest.simulateDeviceDisconnection(testDevice);
+              }),
+      properties);
+}
+
+// A utility function to create an output context as required by non-immersive
+// sessions.
+// https://immersive-web.github.io/webxr/#xrsessioncreationoptions-interface
+function getOutputContext() {
+  let outputCanvas = document.createElement('canvas');
+  document.body.appendChild(outputCanvas);
+  return outputCanvas.getContext('xrpresent');
+}
+
 // This functions calls a callback with each API object as specified
 // by https://immersive-web.github.io/webxr/spec/latest/, allowing
 // checks to be made on all ojects.
@@ -59,8 +127,13 @@
 
   let chain = Promise.resolve();
   ['/gen/layout_test_data/mojo/public/js/mojo_bindings.js',
-   '/gen/ui/gfx/geometry/mojo/geometry.mojom.js',
    '/gen/mojo/public/mojom/base/time.mojom.js',
+   '/gen/gpu/ipc/common/mailbox_holder.mojom.js',
+   '/gen/gpu/ipc/common/sync_token.mojom.js',
+   '/gen/ui/display/mojo/display.mojom.js',
+   '/gen/ui/gfx/geometry/mojo/geometry.mojom.js',
+   '/gen/ui/gfx/mojo/gpu_fence_handle.mojom.js',
+   '/gen/ui/gfx/mojo/transform.mojom.js',
    '/gen/device/vr/public/mojom/vr_service.mojom.js',
    '/resources/chromium/webxr-test.js', '/resources/testdriver.js',
    '/resources/testdriver-vendor.js',
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/webGLCanvasContext_create_with_xrdevice.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/webGLCanvasContext_create_with_xrdevice.https.html
new file mode 100644
index 0000000..94731cc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/webGLCanvasContext_create_with_xrdevice.https.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas id="webgl-canvas"></canvas>
+  <script>
+    xr_promise_test("webglCanvasContext can be created with an XRDevice",
+      (t) => {
+        return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+          .then( (controller) => { return navigator.xr.requestDevice() })
+          .then( (device) => {
+            webglCanvas = document.getElementById('webgl-canvas');
+            gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
+            assert_equals(gl.getContextAttributes().compatibleXRDevice, device);
+
+            // Check that an offscreen context behaves no different.
+            let offscreenCanvas = document.createElement('canvas');
+            let offscreenGl = webglCanvas.getContext(
+              'webgl', {compatibleXRDevice: device});
+            assert_equals(
+              offscreenGl.getContextAttributes().compatibleXRDevice, device);
+          });
+      });
+
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/webGLCanvasContext_setdevice_contextlost.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/webGLCanvasContext_setdevice_contextlost.https.html
new file mode 100644
index 0000000..25f9869b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/webGLCanvasContext_setdevice_contextlost.https.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas id="webgl-canvas"></canvas>
+  <script>
+
+    xr_promise_test(
+      "A lost webglCanvasContext should not be able to set device",
+     (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+        .then( (controller) => { return navigator.xr.requestDevice() })
+        .then( (device) => {
+          webglCanvas = document.getElementById('webgl-canvas');
+          gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
+          gl.getExtension('WEBGL_lose_context').loseContext();
+          return promise_rejects(t, 'InvalidStateError',
+              gl.setCompatibleXRDevice(device));
+        });
+    });
+
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive.https.html
new file mode 100644
index 0000000..e9e7b9bc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+  <script>
+    xr_session_promise_test(
+      "Tests requestSession resolves when supported",
+      (session) => {
+        assert_not_equals(session, null);
+      }, { supportsImmersive:true }, { immersive: true });
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive_no_gesture.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive_no_gesture.https.html
new file mode 100644
index 0000000..9f0f5f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive_no_gesture.https.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <script>
+    xr_promise_test(
+      "Requesting immersive session outside of a user gesture rejects",
+      (t) => {
+        return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+          .then( (controller) => { return navigator.xr.requestDevice() })
+          .then( (device) => promise_rejects(
+            t, 'SecurityError', device.requestSession({ immersive: true })));
+      });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive_unsupported.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive_unsupported.https.html
new file mode 100644
index 0000000..e0d2688e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_immersive_unsupported.https.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <script>
+    xr_promise_test(
+      "Requesting an immersive session when unsupported rejects",
+      (t) => {
+        return XRTest.simulateDeviceConnection({ supportsImmersive:false })
+          .then( (controller) => { return navigator.xr.requestDevice() })
+          .then( (magicWindowOnlyDevice) => new Promise((resolve) => {
+            XRTest.simulateUserActivation( () => {
+              resolve(promise_rejects(
+                t,
+                "NotSupportedError",
+                magicWindowOnlyDevice.requestSession({ immersive: true })
+              ))
+            });
+          }));
+      });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html
new file mode 100644
index 0000000..1634dfe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_requestSession_non_immersive_no_gesture.https.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <script>
+    xr_promise_test(
+      "Requesting non-immersive session outside of a user gesture succeeds",
+      (t) => {
+        return XRTest.simulateDeviceConnection({ supportsImmersive:false })
+          .then( (controller) => { return navigator.xr.requestDevice(); })
+          .then( (device) => device.requestSession({
+            immersive: false,
+            outputContext: getOutputContext()
+          }))
+          .then( (session) => { assert_not_equals(session, null); });
+      });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_immersive.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_immersive.https.html
new file mode 100644
index 0000000..3ca5eb11
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_immersive.https.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <script>
+    xr_promise_test(
+      "supportsSession resolves when immersive options supported",
+      () => {
+        return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+          .then( (controller) => { return navigator.xr.requestDevice() })
+          .then( (device) => device.supportsSession({ immersive: true }));
+      });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_immersive_unsupported.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
new file mode 100644
index 0000000..0c1dd258
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_immersive_unsupported.https.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <script>
+    xr_promise_test(
+      "supportsSession rejects when options not supported",
+      (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:false })
+        .then( (controller) => { return navigator.xr.requestDevice() })
+        .then( (device) => {
+          return promise_rejects(
+            t,
+            "NotSupportedError",
+            device.supportsSession({ immersive: true })
+          );
+        });
+    });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_non_immersive.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_non_immersive.https.html
new file mode 100644
index 0000000..535b0bc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrDevice_supportsSession_non_immersive.https.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <script>
+    xr_promise_test(
+      "supportsSession resolves when non-immersive options supported",
+      (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+        .then( (controller) => { return navigator.xr.requestDevice() })
+        .then( (device) => {
+          // Non-immersive sessions without a outputContext should not be supported.
+          promise_rejects(t, 'NotSupportedError', device.supportsSession());
+
+          // Non-immersive sessions with an outputContext should be supported.
+          return device.supportsSession({
+              outputContext: getOutputContext()
+          });
+        });
+    });
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_cancelAnimationFrame.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_cancelAnimationFrame.https.html
new file mode 100644
index 0000000..26c0e95
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_cancelAnimationFrame.https.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    let immersiveTestName = "XRSession requestAnimationFrame callbacks can be "
+      + "unregistered with cancelAnimationFrame for immersive sessions";
+    let nonImmersiveTestName = "XRSession requestAnimationFrame callbacks can be "
+      + "unregistered with cancelAnimationFrame for non-immersive sessions";
+
+    let fakeDeviceInitParams = { supportsImmersive:true };
+
+    let immersiveSessionOptions = { immersive: true };
+    let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    let testFunction = (session) => new Promise((resolve, reject) => {
+
+      // Schedule and immediately cancel animation frame
+      session.cancelAnimationFrame(session.requestAnimationFrame(
+        (time, vrFrame) => { reject("Cancelled frame callback was called"); }));
+
+      let counter = 0;
+      let handle;
+      function onFrame(time, vrFrame) {
+        // Cancel the second animation frame.
+        if (handle != 0) {
+          session.cancelAnimationFrame(handle);
+          handle = 0;
+        }
+
+        // Run a few more animation frames to be sure that the cancelled frame isn't
+        // going to call.
+        counter++;
+        if (counter >= 4) {
+          // Ok, we're done here.
+          resolve();
+        } else {
+          session.requestAnimationFrame(onFrame);
+        }
+      }
+
+      // Schedule two animation frame and cancel one during on animation frame.
+      session.requestAnimationFrame(onFrame);
+      handle = session.requestAnimationFrame(
+        (time, vrFrame) => { reject("Cancelled frame callback was called"); });
+    });
+
+    xr_session_promise_test(immersiveTestName, testFunction,
+      fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(nonImmersiveTestName, testFunction,
+      fakeDeviceInitParams, nonImmersiveSessionOptions);
+
+  </script>
+</body>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html
new file mode 100644
index 0000000..4f4b8dfe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_cancelAnimationFrame_invalidhandle.https.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+  <script>
+    let immersiveTestName = "XRSession cancelAnimationFrame does not have unexpected "
+      + "behavior when given invalid handles on immersive testSession";
+    let nonImmersiveTestName = "XRSession cancelAnimationFrame does not have unexpected "
+      + "behavior when given invalid handles on non-immersive testSession";
+
+    let fakeDeviceInitParams = { supportsImmersive:true };
+
+    let immersiveSessionOptions = { immersive: true };
+    let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    let testFunction = (testSession) => new Promise((resolve) => {
+      let counter = 0;
+
+      function onFrame(time, vrFrame) {
+        if(counter <= 10) {
+          testSession.requestAnimationFrame(onFrame);
+        } else {
+          resolve();
+        }
+        counter++;
+      }
+
+      let handle = testSession.requestAnimationFrame(onFrame);
+      testSession.cancelAnimationFrame(0);
+      testSession.cancelAnimationFrame(-1);
+      testSession.cancelAnimationFrame(handle + 1);
+      testSession.cancelAnimationFrame(handle - 1);
+      testSession.cancelAnimationFrame(0.5);
+      testSession.cancelAnimationFrame(null);
+    });
+
+    xr_session_promise_test(
+      immersiveTestName, testFunction, fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(
+      nonImmersiveTestName, testFunction, fakeDeviceInitParams, nonImmersiveSessionOptions);
+
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_device.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_device.https.html
new file mode 100644
index 0000000..0b482f5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_device.https.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    xr_promise_test("Requested session has device set",
+     (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+        .then( (controller) => { return navigator.xr.requestDevice() })
+        .then( (device) => new Promise((resolve) => {
+          XRTest.simulateUserActivation( () => {
+            resolve(device.requestSession({ immersive: true }).then( (session) => {
+              assert_true(session.immersive);
+              assert_equals(session.device, device);
+            }));
+          });
+        }));
+    });
+  </script>
+</body>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_end.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_end.https.html
new file mode 100644
index 0000000..69bbda8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_end.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    const immersivetestName = "end event fires when immersive session ends";
+    const nonimmersiveTestName = "end event fires when non-immersive session ends";
+    let watcherDone = new Event("watcherdone");
+    const fakeDeviceInitParams = { supportsImmersive:true };
+
+    const immersiveSessionOptions = { immersive: true };
+    const nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    let testFunction = function(session, testDeviceController, t) {
+      let eventWatcher = new EventWatcher(t, session, ["end", "watcherdone"]);
+      let eventPromise = eventWatcher.wait_for(["end", "watcherdone"]);
+
+      function onSessionEnd(event) {
+        t.step( () => {
+          assert_equals(event.session, session);
+          session.dispatchEvent(watcherDone);
+        });
+      }
+      session.addEventListener("end", onSessionEnd, false);
+      session.end();
+
+      return eventPromise;
+    };
+
+    xr_session_promise_test(immersivetestName, testFunction,
+      fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(nonimmersiveTestName, testFunction,
+      fakeDeviceInitParams, nonImmersiveSessionOptions);
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_exclusive_requestAnimationFrame.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_exclusive_requestAnimationFrame.https.html
deleted file mode 100644
index 010ab0b1..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_exclusive_requestAnimationFrame.https.html
+++ /dev/null
@@ -1,91 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <script src=/resources/testharness.js></script>
-  <script src=/resources/testharnessreport.js></script>
-  <script src="resources/webxr_util.js"></script>
-  <canvas id="webgl-canvas"></canvas>
-
-  <script>
-
-  const identityMatrix = new Float32Array([
-    1, 0, 0, 0,
-    0, 1, 0, 0,
-    0, 0, 1, 0,
-    0, 0, 0, 1
-  ]);
-
-  const rightFakeXRViewInit =
-    {eye:"right", projectionMatrix: identityMatrix, viewMatrix: identityMatrix};
-
-  const leftFakeXRViewInit =
-    {eye:"left", projectionMatrix: identityMatrix, viewMatrix: identityMatrix};
-
-  const immersiveFakeXRDeviceInit = { supportsImmersive:true };
-
-  const webglCanvas = document.getElementById('webgl-canvas');
-  let gl = webglCanvas.getContext('webgl', { alpha: false, antialias: false });
-
-  let testDevice;
-  let testDeviceController;
-  let testSession;
-
-  xr_promise_test(
-    (t) => XRTest.simulateDeviceConnection(immersiveFakeXRDeviceInit)
-      .then((controller) => {
-        testDeviceController = controller;
-        return navigator.xr.requestDevice();
-      })
-      .then((device) => {
-        testDevice = device;
-        return gl.setCompatibleXRDevice(device);
-      })
-      .then(() => new Promise((resolve, reject) => {
-          // Perform the session request in a user gesture.
-          XRTest.simulateUserActivation(() => {
-            testDevice.requestSession({ immersive: true })
-              .then((session) => {
-                testSession = session;
-                return session.requestFrameOfReference('eye-level');
-              })
-              .then((frameOfRef) => {
-                // Session must have a baseLayer or frame requests will be ignored.
-                testSession.baseLayer = new XRWebGLLayer(testSession, gl);
-
-                function onFrame(time, xrFrame) {
-                  assert_true(xrFrame instanceof XRFrame);
-
-                  assert_not_equals(xrFrame.views, null);
-                  assert_equals(xrFrame.views.length, 2);
-
-                  let devicePose = xrFrame.getDevicePose(frameOfRef);
-
-                  assert_not_equals(devicePose, null);
-                  for(let i = 0; i < identityMatrix.length; i++) {
-                    assert_equals(devicePose.poseModelMatrix[i], identityMatrix[i]);
-                  }
-
-                  assert_not_equals(devicePose.getViewMatrix(xrFrame.views[0]), null);
-                  assert_equals(devicePose.getViewMatrix(xrFrame.views[0]).length, 16);
-                  assert_not_equals(devicePose.getViewMatrix(xrFrame.views[1]), null);
-                  assert_equals(devicePose.getViewMatrix(xrFrame.views[1]).length, 16);
-
-                  // Test does not complete until the returned promise resolves.
-                  resolve();
-                }
-
-                testDeviceController.setXRPresentationFrameData(
-                  identityMatrix,
-                  [rightFakeXRViewInit, leftFakeXRViewInit]
-                );
-
-                testSession.requestAnimationFrame(onFrame);
-              }).catch((err) => {
-                reject("Session was rejected with error: "+err);
-              });
-          });
-        })
-      ),
-    "RequestAnimationFrame resolves with good data"
-  );
-  </script>
-</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_prevent_multiple_exclusive.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_prevent_multiple_exclusive.https.html
new file mode 100644
index 0000000..ff3eaef
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_prevent_multiple_exclusive.https.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    xr_promise_test(
+      "Test prevention of multiple simultaneous immersive sessions",
+      (t) => {
+      return XRTest.simulateDeviceConnection({ supportsImmersive:true })
+        .then( (controller) => { return navigator.xr.requestDevice() })
+        .then( (device) => new Promise((resolve) => {
+          XRTest.simulateUserActivation( () => {
+            resolve(device.requestSession({ immersive: true })
+              .then( (session) => new Promise((resolve) => {
+                XRTest.simulateUserActivation( () => {
+                  // Requesting a second immersive session from a device that already
+                  // has an active immersive session should fail. Immersive sessions
+                  // should take up the users entire view, and therefore it should
+                  // be impossible for a user to be engaged with more than one.
+                  resolve(promise_rejects(
+                    t,
+                    "InvalidStateError",
+                    device.requestSession({ immersive: true })
+                  ).then( () => {
+                      // End the immersive session and try again. Now the immersive
+                      // session creation should succeed.
+                      return session.end().then( () => new Promise((resolve) => {
+                        XRTest.simulateUserActivation( () => {
+                          resolve(device.requestSession({ immersive: true }));
+                        });
+                      }));
+                    }));
+                });
+            })));
+          });
+        }));
+    });
+
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https.html
new file mode 100644
index 0000000..48dc887
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_callback_calls.https.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    let immersiveTestName = "XRSession requestAnimationFrame calls the " +
+      "provided callback for an immersive session";
+    let nonImmersiveTestName = "XRSession requestAnimationFrame calls the " +
+      "provided callback a non-immersive session";
+
+    let fakeDeviceInitParams = { supportsImmersive:true };
+
+    let immersiveSessionOptions = { immersive: true };
+    let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    let testFunction = (testSession) => new Promise((resolve) => {
+      function onFrame(time, xrFrame) {
+        assert_true(xrFrame instanceof XRFrame);
+        // Test does not complete until the returned promise resolves.
+        resolve();
+      }
+
+      testSession.requestAnimationFrame(onFrame);
+    });
+
+    xr_session_promise_test(immersiveTestName, testFunction,
+      fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(nonImmersiveTestName, testFunction,
+      fakeDeviceInitParams, nonImmersiveSessionOptions);
+
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https.html
new file mode 100644
index 0000000..702932a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_data_valid.https.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+    const testName = "RequestAnimationFrame resolves with good data";
+
+    const identityMatrix = new Float32Array([
+      1, 0, 0, 0,
+      0, 1, 0, 0,
+      0, 0, 1, 0,
+      0, 0, 0, 1
+    ]);
+
+    const rightFakeXRViewInit = {
+      eye:"right",
+      projectionMatrix: identityMatrix,
+      viewMatrix: identityMatrix
+    };
+
+    const leftFakeXRViewInit = {
+      eye:"left",
+      projectionMatrix: identityMatrix,
+      viewMatrix: identityMatrix
+    };
+
+    const fakeDeviceInitOptions = { supportsImmersive:true };
+    const sessionOptions = { immersive:true };
+
+    let testSession;
+
+    let testFunction = function(session, testDeviceController) {
+      testSession = session;
+      return session.requestFrameOfReference('eye-level')
+        .then((frameOfRef) => new Promise((resolve) => {
+
+          function onFrame(time, xrFrame) {
+            assert_true(xrFrame instanceof XRFrame);
+
+            assert_not_equals(xrFrame.views, null);
+            assert_equals(xrFrame.views.length, 2);
+
+            let devicePose = xrFrame.getDevicePose(frameOfRef);
+
+            assert_not_equals(devicePose, null);
+            for(let i = 0; i < identityMatrix.length; i++) {
+              assert_equals(devicePose.poseModelMatrix[i], identityMatrix[i]);
+            }
+
+            assert_not_equals(devicePose.getViewMatrix(xrFrame.views[0]), null);
+            assert_equals(devicePose.getViewMatrix(xrFrame.views[0]).length, 16);
+            assert_not_equals(devicePose.getViewMatrix(xrFrame.views[1]), null);
+            assert_equals(devicePose.getViewMatrix(xrFrame.views[1]).length, 16);
+
+            // Test does not complete until the returned promise resolves.
+            resolve();
+          }
+
+          testDeviceController.setXRPresentationFrameData(
+            identityMatrix,
+            [rightFakeXRViewInit, leftFakeXRViewInit]
+          );
+
+          testSession.requestAnimationFrame(onFrame);
+        })
+      );
+    }
+
+    xr_session_promise_test(
+      testName, testFunction, fakeDeviceInitOptions, sessionOptions);
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_getDevicePose.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_getDevicePose.https.html
new file mode 100644
index 0000000..1745211
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestAnimationFrame_getDevicePose.https.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+
+  <script>
+
+    let immersiveTestName =
+      "XRFrame getDevicePose updates on the next frame for immersive sessions";
+    let nonImmersiveTestName =
+      "XRFrame getDevicePose updates on the next frame for non-immersive sessions";
+
+    let fakeDeviceInitParams = { supportsImmersive: true };
+
+    let immersiveSessionOptions = { immersive: true };
+    let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    // Valid matrices for  when we don't care about specific values
+    const validPoseMatrix = [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1];
+    const validProjectionMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 3, 2, -1, -1, 0, 0, -0.2, 0];
+    const validViewMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 3, 2, 1];
+
+    let testFunction = function(session, fakeDeviceController, t) {
+      return session.requestFrameOfReference("eye-level")
+        .then((frameOfRef) => new Promise((resolve, reject) => {
+          let counter = 0;
+          function onFrame(time, vrFrame) {
+            session.requestAnimationFrame(onFrame);
+            if (counter == 0) {
+              t.step( () => {
+                // Expecting to not get a pose since none has been supplied
+                assert_equals(vrFrame.getDevicePose(frameOfRef), null);
+
+                fakeDeviceController.setXRPresentationFrameData(
+                  validPoseMatrix, [{
+                    eye:"left",
+                    projectionMatrix: validProjectionMatrix,
+                    viewMatrix: validViewMatrix
+                  }, {
+                    eye:"right",
+                    projectionMatrix: validProjectionMatrix,
+                    viewMatrix: validViewMatrix
+                  }]);
+
+                // Check that pose does not update pose within the same frame.
+                assert_equals(vrFrame.getDevicePose(frameOfRef), null);
+              });
+            } else {
+              t.step( () => {
+                let pose = vrFrame.getDevicePose(frameOfRef);
+                assert_not_equals(pose, null);
+
+                let poseMatrix = pose.poseModelMatrix;
+                assert_not_equals(poseMatrix, null);
+
+                for(let i = 0; i < poseMatrix.length; i++) {
+                  assert_equals(poseMatrix[i], validPoseMatrix[i]);
+                }
+              });
+
+              // Finished.
+              resolve();
+            }
+            counter++;
+          }
+
+          session.requestAnimationFrame(onFrame);
+        }));
+    };
+
+    xr_session_promise_test(immersiveTestName, testFunction,
+      fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(nonImmersiveTestName, testFunction,
+      fakeDeviceInitParams, nonImmersiveSessionOptions);
+
+  </script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestFrameOfReference.https.html b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestFrameOfReference.https.html
new file mode 100644
index 0000000..ea75876
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webxr/xrSession_requestFrameOfReference.https.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<body>
+  <script src=/resources/testharness.js></script>
+  <script src=/resources/testharnessreport.js></script>
+  <script src="resources/webxr_util.js"></script>
+  <canvas></canvas>
+  <script>
+
+    let immersiveTestName =
+      "Immersive XRSession requestFrameOfReference returns expected objects";
+    let nonImmersiveTestName =
+      "Non-immersive XRSession requestFrameOfReference returns expected objects";
+
+    let fakeDeviceInitParams = { supportsImmersive: true };
+
+    let immersiveSessionOptions = { immersive: true };
+    let nonImmersiveSessionOptions = { outputContext: getOutputContext() };
+
+    let testFunction = function(session, fakeDeviceController, t) {
+      return promise_rejects(t, new TypeError(), session.requestFrameOfReference("foo"))
+        .then(() => Promise.all([
+          session.requestFrameOfReference("head-model").then( (frameOfRef) => {
+            assert_true(frameOfRef instanceof XRCoordinateSystem,
+              "head-model frameOfRef is not correct type.");
+            assert_true(frameOfRef instanceof XRFrameOfReference,
+              "head-model frameOfRef is not correct type.");
+          }),
+          session.requestFrameOfReference("eye-level").then( (frameOfRef) => {
+            assert_true(frameOfRef instanceof XRCoordinateSystem,
+              "eye-level frameOfRef is not correct type.");
+            assert_true(frameOfRef instanceof XRFrameOfReference,
+              "eye-level frameOfRef is not correct type.");
+          }),
+          session.requestFrameOfReference("stage").then( (frameOfRef) => {
+            assert_true(frameOfRef instanceof XRCoordinateSystem,
+              "stage frameOfRef is not correct type.");
+            assert_true(frameOfRef instanceof XRFrameOfReference,
+              "stage frameOfRef is not correct type.");
+          })
+      ]));
+    };
+
+    xr_session_promise_test(
+      immersiveTestName, testFunction, fakeDeviceInitParams, immersiveSessionOptions);
+    xr_session_promise_test(
+      nonImmersiveTestName, testFunction, fakeDeviceInitParams, nonImmersiveSessionOptions);
+
+  </script>
+</body>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-with-overflowing-object-fit-expected.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-with-overflowing-object-fit-expected.html
new file mode 100644
index 0000000..e430e9a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-with-overflowing-object-fit-expected.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<div style="position:absolute; top:30px; padding:10px; border:5px dashed black; width:100px; height:50px; overflow:hidden;">
+  <div style="position:absolute; left:10px; top:10px; width:50px; height:25px; background:black;"></div>
+  <div style="position:absolute; left:60px; top:35px; width:50px; height:25px; background:black;"></div>
+</div>
+<div>This test passes if the overflowing canvas contents are correctly clipped to the content box.</div>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-with-overflowing-object-fit.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-with-overflowing-object-fit.html
new file mode 100644
index 0000000..47e84393
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-with-overflowing-object-fit.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<canvas width="200" height="200" style="position:absolute; top:30px; padding:10px; border:5px dashed black; object-fit:cover; width:100px; height:50px;"></canvas>
+<div>This test passes if the overflowing canvas contents are correctly clipped to the content box.</div>
+<script>
+var context = document.getElementsByTagName("canvas")[0].getContext("2d");
+context.fillRect(0, 0, 100, 100);
+context.fillRect(100, 100, 100, 100);
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id-expected.txt
index 5f9085e3..e22f627 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id-expected.txt
@@ -1,5 +1,5 @@
 Tests that Tracing agent returns a session id upon a start that is matching one issued in trace events.
 
-Got DevTools metadata event: TracingStartedInPage
+Got DevTools metadata event: TracingStartedInBrowser
 Got DevTools metadata event: SetLayerTreeId
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js b/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js
index ff36ed9..9bade241 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/tracing-session-id.js
@@ -19,20 +19,16 @@
 
   await PerformanceTestRunner.invokeAsyncWithTimeline('waitForRaf');
 
-  PerformanceTestRunner.tracingModel().sortedProcesses().forEach(function(process) {
-    process.sortedThreads().forEach(function(thread) {
-      thread.events().forEach(processEvent);
-    });
-  });
+  let frameId = '';
+  PerformanceTestRunner.tracingModel().sortedProcesses().forEach(process =>
+    process.sortedThreads().forEach(thread => thread.events().forEach(processEvent))
+  );
   TestRunner.completeTest();
 
-  var frameId = '';
-
   function processEvent(event) {
     if (!event.hasCategory(SDK.TracingModel.DevToolsMetadataEventCategory))
       return;
-
-    if (event.name === TimelineModel.TimelineModel.RecordType.TracingStartedInPage) {
+    if (event.name === TimelineModel.TimelineModel.DevToolsMetadataEvent.TracingStartedInBrowser) {
       TestRunner.addResult('Got DevTools metadata event: ' + event.name);
       frameId = event.args['data']['frames'][0]['frame'];
     } else if (event.name === TimelineModel.TimelineModel.RecordType.SetLayerTreeId) {
diff --git a/third_party/WebKit/LayoutTests/paint/input/file-input-clip-expected.html b/third_party/WebKit/LayoutTests/paint/input/file-input-clip-expected.html
new file mode 100644
index 0000000..8c7b96b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/input/file-input-clip-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<div style="padding: 2px; border: 10px solid blue; width: 30px; overflow: hidden">
+  <input type="file" style="display: block">
+</div>
diff --git a/third_party/WebKit/LayoutTests/paint/input/file-input-clip.html b/third_party/WebKit/LayoutTests/paint/input/file-input-clip.html
new file mode 100644
index 0000000..1b7e05f2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/input/file-input-clip.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<input type="file" style="padding: 2px; display: block; border: 10px solid blue; width: 30px">
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
new file mode 100644
index 0000000..5a7b80a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS The start time of a newly-created transition is unresolved
+FAIL The start time of transitions is based on when they are generated assert_less_than: A CSS transition added in a later frame has a later start time expected a number less than 43.924000001425156 but got 43.924000001425156
+PASS The start time of a transition can be set
+PASS The start time can be set to seek a transition
+FAIL Seeking a transition using start time dispatches transition events promise_test: Unhandled rejection with value: object "Error: Timed out waiting for Promise to resolve: transitionstart"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
new file mode 100644
index 0000000..a76894c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/external/wpt/css/css-transitions/CSSTransition-startTime.tentative-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+PASS The start time of a newly-created transition is unresolved
+FAIL The start time of transitions is based on when they are generated assert_less_than: A CSS transition added in a later frame has a later start time expected a number less than 42.76899999968009 but got 42.76899999968009
+PASS The start time of a transition can be set
+PASS The start time can be set to seek a transition
+FAIL Seeking a transition using start time dispatches transition events promise_test: Unhandled rejection with value: object "Error: Timed out waiting for Promise to resolve: transitionstart"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid-expected.txt
new file mode 100644
index 0000000..ee8dbc23
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Grid assert_equals: expected "86px" but got "162px"
+FAIL Inline grid assert_equals: expected "86px" but got "162px"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/platform/win/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid-expected.txt b/third_party/WebKit/LayoutTests/platform/win/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid-expected.txt
new file mode 100644
index 0000000..818124e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/external/wpt/html/rendering/non-replaced-elements/the-fieldset-element-0/fieldset-grid-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Grid assert_equals: expected "90px" but got "180px"
+FAIL Inline grid assert_equals: expected "90px" but got "180px"
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animation-worklet-inside-iframe.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animation-worklet-inside-iframe.html
index df1f474..a625a9b 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animation-worklet-inside-iframe.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animation-worklet-inside-iframe.html
@@ -4,7 +4,6 @@
   width: 100px;
   height: 100px;
   background-color: #ff0000;
-  will-change: opacity;
 }
 </style>
 <script id="main_worklet" type="text/worklet">
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-animate.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-animate.html
index d22b0fe..784cc6d 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-animate.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-animate.html
@@ -20,7 +20,7 @@
 });
 </script>
 
-<div id="target" style="will-change: transform, opacity;"></div>
+<div id="target"></div>
 
 <script src="resources/animation-worklet-tests.js"></script>
 <script>
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-with-options.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-with-options.html
index 84903d4..d7d0dbe 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-with-options.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/animator-with-options.html
@@ -23,7 +23,7 @@
 });
 </script>
 
-<div id="target" style="will-change: transform, opacity;"></div>
+<div id="target"></div>
 
 <script src="resources/animation-worklet-tests.js"></script>
 <script>
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/resources/animator-iframe.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/resources/animator-iframe.html
index 48274a9..f15991c 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/resources/animator-iframe.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/resources/animator-iframe.html
@@ -4,7 +4,6 @@
   width: 100px;
   height: 100px;
   background-color: #00ff00;
-  will-change: opacity;
 }
 </style>
 <script src="animation-worklet-tests.js"></script>
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-cancel.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-cancel.html
index bd6df3c4..41b7e66f2 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-cancel.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-cancel.html
@@ -4,7 +4,6 @@
   width: 100px;
   height: 100px;
   background-color: #0000ff;
-  will-change: transform;  /* force compositing */
 }
 </style>
 
diff --git a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-style-update.html b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-style-update.html
index 7a45b56..25f18d45 100644
--- a/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-style-update.html
+++ b/third_party/WebKit/LayoutTests/virtual/threaded/fast/animationworklet/worklet-animation-style-update.html
@@ -4,11 +4,6 @@
   width: 100px;
   height: 100px;
   background-color: #00ff00;
-  /*
-  * Force compositing.
-  * TODO(majidvp): Should not be needed when http://crbug.com/776533 is fixed.
-  */
-  will-change: transform, opacity;
 }
 
 </style>
diff --git a/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js b/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js
index 4ade3cc..42224d7 100644
--- a/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js
+++ b/third_party/WebKit/LayoutTests/vr/resources/vr-test-utils.js
@@ -1,13 +1,17 @@
 'use strict';
 
 MockVRService.prototype.setListeningForActivate = function(client) {
-  for (let i = 0; i < this.devices_.length; i++) {
-    this.devices_[i].displayClient_ = client;
+  for (let i = 0; i < this.runtimes_.length; i++) {
+    this.runtimes_[i].displayClient_ = client;
   }
 };
 
+MockVRService.prototype.getImmersiveVRDisplayInfo = function() {
+  return Promise.resolve(
+      {info: this.runtimes_[0] ? this.runtimes_[0].displayInfo_ : null});
+};
 
-MockDevice.prototype.setPose = function(pose) {
+MockRuntime.prototype.setPose = function(pose) {
   if (pose == null) {
     this.pose_ = null;
   } else {
@@ -30,14 +34,10 @@
   }
 };
 
-MockDevice.prototype.forceActivate = function(reason) {
+MockRuntime.prototype.forceActivate = function(reason) {
   this.displayClient_.onActivate(reason);
 };
 
-MockDevice.prototype.getImmersiveVRDisplayInfo = function() {
-  return Promise.resolve({info: this.displayInfo_});
-};
-
 function vr_test(func, vrDisplays, name, properties) {
   let chain = Promise.resolve();
   let firstDeviceController;
diff --git a/third_party/WebKit/LayoutTests/xr/events_session_end.html b/third_party/WebKit/LayoutTests/xr/events_session_end.html
deleted file mode 100644
index a1092f4..0000000
--- a/third_party/WebKit/LayoutTests/xr/events_session_end.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-let testName = "Test end fires when session ends";
-
-let watcherDone = new Event("watcherdone");
-
-let fakeDeviceInitParams = { supportsImmersive:true };
-
-let requestSessionOptions = [
-  { immersive: true },
-  { outputContext: getOutputContext() }
-];
-
-let testFunction = function(session, t) {
-  let eventWatcher = new EventWatcher(t, session, ["end", "watcherdone"]);
-  let eventPromise = eventWatcher.wait_for(["end", "watcherdone"]);
-
-  function onSessionEnd(event) {
-    t.step( () => {
-      assert_equals(event.session, session);
-      session.dispatchEvent(watcherDone);
-    });
-  }
-  session.addEventListener("end", onSessionEnd, false);
-  session.end();
-
-  return eventPromise;
-};
-
-xr_session_promise_test(
-  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html
deleted file mode 100644
index cdebdb8b..0000000
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_called.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-let testName = "XRSession requestAnimationFrame properly calls the provided "
-  + "callback";
-
-let fakeDeviceInitParams = { supportsImmersive:true };
-
-let requestSessionOptions = [
-  { immersive: true },
-  { outputContext: getOutputContext() }
-];
-
-let testFunction = (session) => new Promise((resolve) => {
-  // Session must have a baseLayer or frame requests will be ignored.
-  session.baseLayer = new XRWebGLLayer(session, gl);
-
-  function onFrame(time, xrFrame) {
-    assert_true(xrFrame instanceof XRFrame);
-    // Test does not complete until the returned promise resolves.
-    resolve();
-  }
-
-  session.requestAnimationFrame(onFrame);
-});
-
-xr_session_promise_test(
-  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html
deleted file mode 100644
index 7e9b66eb..0000000
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_invalidhandle.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-let testName = "XRSession cancelAnimationFrame does not have unexpected "
-  + "behavior when given invalid handles";
-
-let fakeDeviceInitParams = { supportsImmersive:true };
-
-let requestSessionOptions = [
-  { immersive: true },
-  { outputContext: getOutputContext() }
-];
-
-let testFunction = (session) => new Promise((resolve) => {
-  // Session must have a baseLayer or frame requests will be ignored.
-  session.baseLayer = new XRWebGLLayer(session, gl);
-
-  let counter = 0;
-
-  function onFrame(time, vrFrame) {
-    // Intentionally session.requestAnimationFrame at the beginning, ensuring that
-    // there's an outstanding callback when the test completes. This is to make
-    // sure it doesn't cause any unexpected behavior like it did with
-    // crbug.com/679401
-    session.requestAnimationFrame(onFrame);
-    if (counter > 10) {
-      resolve();
-    }
-    counter++;
-  }
-
-  let handle = session.requestAnimationFrame(onFrame);
-  session.cancelAnimationFrame(0);
-  session.cancelAnimationFrame(-1);
-  session.cancelAnimationFrame(handle + 1);
-  session.cancelAnimationFrame(handle - 1);
-  session.cancelAnimationFrame(0.5);
-  session.cancelAnimationFrame(null);
-});
-
-xr_session_promise_test(
-  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html b/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html
deleted file mode 100644
index 5cdbfce..0000000
--- a/third_party/WebKit/LayoutTests/xr/exclusive_requestFrame_unregister.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-let testName = "XRSession requestAnimationFrame callbacks can be unregistered "
-  + "with cancelAnimationFrame";
-
-let fakeDeviceInitParams = { supportsImmersive:true };
-
-let requestSessionOptions = [
-  { immersive: true },
-  { outputContext: getOutputContext() }
-];
-
-let testFunction = (session) => new Promise((resolve, reject) => {
-  // Session must have a baseLayer or frame requests will be ignored.
-  session.baseLayer = new XRWebGLLayer(session, gl);
-
-  function onFrameBad(time, vrFrame) {
-    reject("Unregistered callback was called");
-  }
-
-  let counter = 0;
-  let handle2 = 0;
-  function onFrameGood(time, vrFrame) {
-    counter++;
-    if (counter >= 4) {
-      resolve();
-      // Intentionally don't return immediately so that session.requestAnimationFrame
-      // gets called again to make sure it doesn't cause unexpected behavior
-      // like in crbug.com/679401
-    }
-    session.requestAnimationFrame(onFrameGood);
-
-    if (handle2 != 0) {
-      // The first time we enter this callback the callback associated with
-      // handle2 will have already been queued to execute immediately after
-      // this callback returns. Ensure that cancelAnimationFrame works even in that
-      // scenario.
-      session.cancelAnimationFrame(handle2);
-      handle2 = 0;
-    }
-  }
-
-  let handle = session.requestAnimationFrame(onFrameBad);
-  session.cancelAnimationFrame(handle);
-  session.requestAnimationFrame(onFrameGood);
-  handle2 = session.requestAnimationFrame(onFrameBad);
-});
-
-xr_session_promise_test(
-  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html b/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
deleted file mode 100644
index 43885bd..0000000
--- a/third_party/WebKit/LayoutTests/xr/getDevicePose_oneframeupdate.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-let testName = "XRFrame getDevicePose updates on the next frame";
-
-let fakeDeviceInitParams = { supportsImmersive: true };
-
-let requestSessionOptions =  [
-  { immersive: true },
-  { outputContext: getOutputContext() }
-];
-
-let testFunction = function(session, t, fakeDeviceController) {
-  // Session must have a baseLayer or else frame requests will be ignored.
-  session.baseLayer = new XRWebGLLayer(session, gl);
-
-  return session.requestFrameOfReference("eye-level")
-    .then((frameOfRef) => new Promise((resolve, reject) => {
-      let counter = 0;
-      function onFrame(time, vrFrame) {
-        session.requestAnimationFrame(onFrame);
-        if (counter == 0) {
-          t.step( () => {
-            // Expecting to not get a pose since none has been supplied
-            assert_equals(vrFrame.getDevicePose(frameOfRef), null);
-
-            // Need to have a valid pose or input event's don't process.
-            fakeDeviceController.setXRPresentationFrameData(
-              VALID_POSE_MATRIX, [{
-                eye:"left",
-                projectionMatrix: VALID_PROJECTION_MATRIX,
-                viewMatrix: VALID_VIEW_MATRIX
-              }, {
-                eye:"right",
-                projectionMatrix: VALID_PROJECTION_MATRIX,
-                viewMatrix: VALID_VIEW_MATRIX
-              }]);
-
-            // Check that pose does not update pose within the same frame.
-            assert_equals(vrFrame.getDevicePose(frameOfRef), null);
-          });
-        } else {
-          t.step( () => {
-            // Check that pose was updated.
-            let pose = vrFrame.getDevicePose(frameOfRef);
-            assert_not_equals(pose, null);
-
-            let poseMatrix = pose.poseModelMatrix;
-            assert_not_equals(poseMatrix, null);
-
-            assert_matrices_approx_equal(poseMatrix, VALID_POSE_MATRIX);
-          });
-
-          // Finished.
-          resolve();
-        }
-        counter++;
-      }
-
-      session.requestAnimationFrame(onFrame);
-    }));
-};
-
-xr_session_promise_test(
-  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/requestDevice_with_device.html b/third_party/WebKit/LayoutTests/xr/requestDevice_with_device.html
deleted file mode 100644
index 6cf7d5f7..0000000
--- a/third_party/WebKit/LayoutTests/xr/requestDevice_with_device.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-
-promise_test((t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive: true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => {
-      t.step(() => {
-        assert_true(device != null);
-        assert_true(device instanceof XRDevice);
-      });
-    });
-}, "navigator.xr.requestDevice properly returns a single device");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/requestDevice_without_device.html b/third_party/WebKit/LayoutTests/xr/requestDevice_without_device.html
deleted file mode 100644
index b273a87..0000000
--- a/third_party/WebKit/LayoutTests/xr/requestDevice_without_device.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-
-promise_test( (t) => {
-  return promise_rejects(t, 'NotFoundError', navigator.xr.requestDevice());
-}, "navigator.xr.requestDevice properly rejects when there are 0 devices");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js b/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js
index bd429a1..19bf18c 100644
--- a/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js
+++ b/third_party/WebKit/LayoutTests/xr/resources/xr-internal-device-mocking.js
@@ -4,9 +4,9 @@
  * for interal tests. The main mocked objects are found in
  * ../external/wpt/resources/chromium/webxr-test.js. */
 
-MockDevice.prototype.base_getFrameData = MockDevice.prototype.getFrameData;
+MockRuntime.prototype.base_getFrameData = MockRuntime.prototype.getFrameData;
 
-MockDevice.prototype.getFrameData = function() {
+MockRuntime.prototype.getFrameData = function() {
   return this.base_getFrameData().then((result) => {
     if (result.frameData && result.frameData.pose && this.input_sources_) {
       let input_states = [];
@@ -21,7 +21,7 @@
   });
 };
 
-MockDevice.prototype.addInputSource = function(source) {
+MockRuntime.prototype.addInputSource = function(source) {
   if (!this.input_sources_) {
     this.input_sources_ = [];
     this.next_input_source_index_ = 1;
@@ -32,7 +32,7 @@
   this.input_sources_.push(source);
 };
 
-MockDevice.prototype.removeInputSource = function(source) {
+MockRuntime.prototype.removeInputSource = function(source) {
   if (!this.input_sources_)
     return;
 
@@ -44,11 +44,11 @@
   }
 };
 
-MockDevice.prototype.setHitTestResults = function(results) {
+MockRuntime.prototype.setHitTestResults = function(results) {
   this.hittest_results_ = results;
 };
 
-MockDevice.prototype.requestHitTest = function(ray) {
+MockRuntime.prototype.requestHitTest = function(ray) {
   var hit_results = this.hittest_results_;
   if (!hit_results) {
     var hit = new device.mojom.XRHitResult();
@@ -58,13 +58,13 @@
   return Promise.resolve(hit_results);
 };
 
-MockDevice.prototype.setResetPose = function(to) {
+MockRuntime.prototype.setResetPose = function(to) {
   if (this.pose_) {
     this.pose_.poseReset = to;
   }
 };
 
-MockDevice.prototype.setStageTransform = function(value) {
+MockRuntime.prototype.setStageTransform = function(value) {
   if (value) {
     if (!this.displayInfo_.stageParameters) {
       this.displayInfo_.stageParameters = {
@@ -82,11 +82,11 @@
   this.sessionClient_.onChanged(this.displayInfo_);
 };
 
-MockDevice.prototype.getSubmitFrameCount = function() {
+MockRuntime.prototype.getSubmitFrameCount = function() {
   return this.presentation_provider_.submit_frame_count_;
 };
 
-MockDevice.prototype.getMissingFrameCount = function() {
+MockRuntime.prototype.getMissingFrameCount = function() {
   return this.presentation_provider_.missing_frame_count_;
 };
 
diff --git a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html b/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html
deleted file mode 100644
index 9edcc75..0000000
--- a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_create_with_xr_device.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => {
-      webglCanvas = document.getElementById('webgl-canvas');
-      gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-      assert_equals(gl.getContextAttributes().compatibleXRDevice, device);
-
-      // Check that an offscreen context behaves no different.
-      let offscreenCanvas = document.createElement('canvas');
-      let offscreenGl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-      assert_equals(offscreenGl.getContextAttributes().compatibleXRDevice, device);
-    });
-}, "A webglCanvasContext created with an XRDevice has that device set");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html b/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html
deleted file mode 100644
index 47c7c99..0000000
--- a/third_party/WebKit/LayoutTests/xr/webGLCanvasContext_set_device_lost_context.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => {
-      webglCanvas = document.getElementById('webgl-canvas');
-      gl = webglCanvas.getContext('webgl', {compatibleXRDevice: device});
-      gl.getExtension('WEBGL_lose_context').loseContext();
-      return promise_rejects(t, 'InvalidStateError',
-          gl.setCompatibleXRDevice(device));
-    });
-}, "A lost webglCanvasContext should not be able to set device");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html
deleted file mode 100644
index 2f1d5ad5..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_no_gesture.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => promise_rejects(
-      t, 'SecurityError', device.requestSession({ immersive: true })));
-}, "request immersive session outside of a user gesture rejects");
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html
deleted file mode 100644
index e3a3a99..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_supported.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-
-xr_session_promise_test( (session) => {
-  assert_not_equals(session, null);
-}, { supportsImmersive:true }, [{ immersive: true }],
-"requestSession for an immersive session with a user gesture resolves");
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html b/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html
deleted file mode 100644
index f41ce4c..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_requestSession_exclusive_unsupported.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:false })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (magicWindowOnlyDevice) => new Promise((resolve) => {
-      runWithUserGesture( () => {
-        resolve(promise_rejects(
-          t,
-          "NotSupportedError",
-          magicWindowOnlyDevice.requestSession({ immersive: true })
-        ))
-      });
-    }));
-}, "requesting an immersive session on an unsupported device rejects");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html
deleted file mode 100644
index d3f016d..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_rejects.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:false })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (magicWindowOnlyDevice) => {
-      return promise_rejects(
-        t,
-        "NotSupportedError",
-        magicWindowOnlyDevice.supportsSession({ immersive: true })
-      );
-    });
-}, "supportsSession rejects when immersive session is not supported on device");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html
deleted file mode 100644
index 8b398ba..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_exclusive_resolves.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-
-promise_test( () => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => device.supportsSession({ immersive: true }));
-}, "supportsSession resolves when support immersive session is supported on device");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html b/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html
deleted file mode 100644
index 7082623e..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrDevice_supportsSession_non_exclusive.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<script>
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => {
-      // Non-immersive sessions without a outputContext should not be supported.
-      promise_rejects(t, 'NotSupportedError', device.supportsSession());
-
-      // Non-immersive sessions with an outputContext should be supported.
-      return device.supportsSession({
-          outputContext: getOutputContext()
-      });
-    });
-}, "supportsSession properly identifies supported non-immersive sessions");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html b/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html
deleted file mode 100644
index 2ac44504..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrSession_exclusive.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => new Promise((resolve) => {
-      runWithUserGesture( () => {
-        resolve(device.requestSession({ immersive: true }).then( (session) => {
-          assert_true(session.immersive);
-          assert_equals(session.device, device);
-          assert_approx_equals(session.depthNear, 0.1, FLOAT_EPSILON);
-          assert_approx_equals(session.depthFar, 1000.0, FLOAT_EPSILON);
-        }));
-      });
-    }));
-}, "supportsSession returns expected immersive session");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html b/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html
deleted file mode 100644
index a9295eb1..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrSession_prevent_multiple_exclusive.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-
-
-promise_test( (t) => {
-  return XRTest.simulateDeviceConnection({ supportsImmersive:true })
-    .then( (controller) => { return navigator.xr.requestDevice() })
-    .then( (device) => new Promise((resolve) => {
-      runWithUserGesture( () => {
-        resolve(device.requestSession({ immersive: true })
-          .then( (session) => new Promise((resolve) => {
-            runWithUserGesture( () => {
-              // Requesting a second immersive session from a device that already
-              // has an active immersive session should fail. Immersive sessions
-              // should take up the users entire view, and therefore it should
-              // be impossible for a user to be engaged with more than one.
-              resolve(promise_rejects(
-                t,
-                "InvalidStateError",
-                device.requestSession({ immersive: true })
-              ).then( () => {
-                  // End the immersive session and try again. Now the immersive
-                  // session creation should succeed.
-                  return session.end().then( () => new Promise((resolve) => {
-                    runWithUserGesture( () => {
-                      resolve(device.requestSession({ immersive: true }));
-                    });
-                  }));
-                }));
-            });
-        })));
-      });
-    }));
-}, "requestSession prevents creation of multiple simultaneous immersive sessions");
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html b/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html
deleted file mode 100644
index dcc5996..0000000
--- a/third_party/WebKit/LayoutTests/xr/xrSession_requestFrameOfReference.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
-<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
-<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
-<script src="../xr/resources/xr-test-utils.js"></script>
-<script src="../xr/resources/test-constants.js"></script>
-<canvas id="webgl-canvas"></canvas>
-
-<script>
-let testName = "requestFrameOfReference returns the expected objects";
-
-let fakeDeviceInitParams = { supportsImmersive:true };
-
-let requestSessionOptions = [
-  { outputContext: getOutputContext() },
-  { immersive: true },
-];
-
-let testFunction = function(session, t) {
-  return promise_rejects(t, new TypeError(), session.requestFrameOfReference("foo"))
-    .then(() => Promise.all([
-      session.requestFrameOfReference("head-model").then( (frameOfRef) => {
-        assert_true(frameOfRef instanceof XRCoordinateSystem,
-          "head-model frameOfRef is not correct type.");
-        assert_true(frameOfRef instanceof XRFrameOfReference,
-          "head-model frameOfRef is not correct type.");
-      }),
-      session.requestFrameOfReference("eye-level").then( (frameOfRef) => {
-        assert_true(frameOfRef instanceof XRCoordinateSystem,
-          "eye-level frameOfRef is not correct type.");
-        assert_true(frameOfRef instanceof XRFrameOfReference,
-          "eye-level frameOfRef is not correct type.");
-      }),
-      session.requestFrameOfReference("stage").then( (frameOfRef) => {
-        assert_true(frameOfRef instanceof XRCoordinateSystem,
-          "stage frameOfRef is not correct type.");
-        assert_true(frameOfRef instanceof XRFrameOfReference,
-          "stage frameOfRef is not correct type.");
-      })
-  ]));
-};
-
-xr_session_promise_test(
-  testFunction, fakeDeviceInitParams, requestSessionOptions, testName);
-
-</script>
diff --git a/third_party/blink/perf_tests/css/CustomPropertiesNonRootInheritance.html b/third_party/blink/perf_tests/css/CustomPropertiesNonRootInheritance.html
new file mode 100644
index 0000000..20586e7
--- /dev/null
+++ b/third_party/blink/perf_tests/css/CustomPropertiesNonRootInheritance.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../resources/runner.js"></script>
+    <script src="resources/utils.js"></script>
+    <style>
+    </style>
+</head>
+<body>
+    <script>
+        for (let i = 0; i < 5000; i++) {
+            let div = document.createElement('div');
+            div.style = 'color: red'; // Avoid matched properties cache.
+            document.body.appendChild(div);
+        }
+
+        let props = [];
+        let value = 2**32;
+        for (let i = 0; i < 1000; i++) {
+            props.push(`--property-with-long-name-and-value-${i}: ${value.toString(2)};`);
+            // Use a unique value every time to avoid optimization that might
+            // utilize identical values.
+            value--;
+        }
+
+        applyCSSRule(`body { ${props.join('\n')} }`);
+        applyCSSRule('body { --x: 0; --y: var(--x); }');
+
+        PerfTestRunner.measureTime({
+            description: 'Measure impact of unused custom properties at body.',
+            run: function() {
+                document.body.style = 'display: none';
+                forceStyleRecalc(document.body);
+                document.body.style = '';
+                forceStyleRecalc(document.body);
+            }
+        });
+    </script>
+</body>
+</html>
diff --git a/third_party/blink/perf_tests/css/CustomPropertiesRootInheritance.html b/third_party/blink/perf_tests/css/CustomPropertiesRootInheritance.html
new file mode 100644
index 0000000..48945d6
--- /dev/null
+++ b/third_party/blink/perf_tests/css/CustomPropertiesRootInheritance.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../resources/runner.js"></script>
+    <script src="resources/utils.js"></script>
+    <style>
+    </style>
+</head>
+<body>
+    <script>
+        for (let i = 0; i < 5000; i++) {
+            let div = document.createElement('div');
+            div.style = 'color: red'; // Avoid matched properties cache.
+            document.body.appendChild(div);
+        }
+
+        let props = [];
+        let value = 2**32;
+        for (let i = 0; i < 1000; i++) {
+            props.push(`--property-with-long-name-and-value-${i}: ${value.toString(2)};`);
+            // Use a unique value every time to avoid optimization that might
+            // utilize identical values.
+            value--;
+        }
+
+        applyCSSRule(`:root { ${props.join('\n')} }`);
+        applyCSSRule(':root { --x: 0; --y: var(--x); }');
+
+        PerfTestRunner.measureTime({
+            description: 'Measure impact of unused custom properties at :root.',
+            run: function() {
+                document.body.style = 'display: none';
+                forceStyleRecalc(document.body);
+                document.body.style = '';
+                forceStyleRecalc(document.body);
+            }
+        });
+    </script>
+</body>
+</html>
diff --git a/third_party/blink/perf_tests/css/CustomPropertiesVarAlias.html b/third_party/blink/perf_tests/css/CustomPropertiesVarAlias.html
new file mode 100644
index 0000000..8083f0a
--- /dev/null
+++ b/third_party/blink/perf_tests/css/CustomPropertiesVarAlias.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../resources/runner.js"></script>
+    <script src="resources/utils.js"></script>
+</head>
+<body>
+    <div id=target></div>
+</body>
+    <script>
+        const propCount = 2000;
+
+        // Create a rule which defines 'propCount' custom properties with a
+        // linear var()-dependency on eachother.
+        function createRule() {
+            let lines = ['#target {'];
+            for (let i = 0; i < propCount; i++) {
+                let value = '#fefefe';
+                if (i > 0) {
+                    value = `var(--prop-${i-1})`;
+                }
+                lines.push(`--prop-${i}: ${value};`);
+            }
+            lines.push('}');
+            return lines.join('\n');
+        }
+
+        applyCSSRule(createRule());
+
+        PerfTestRunner.measureTime({
+            description: 'Measures performance of var()-as-alias resolution',
+            run: function() {
+                target.style = 'display: none';
+                forceStyleRecalc(target);
+                target.style = '';
+                getComputedStyle(target).getPropertyValue('--prop-'+(propCount-1));
+            }
+        });
+    </script>
+</html>
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index 841b998..d2f490e 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -66,9 +66,7 @@
 
  private:
   bool TryGetData(const uint8_t** data, size_t* length) {
-#if DCHECK_IS_ON()
-    DCHECK(mutex_.Locked());
-#endif
+    mutex_.AssertAcquired();
     if (!data_.IsEmpty()) {
       std::pair<const uint8_t*, size_t> next_data = data_.TakeFirst();
       *data = next_data.first;
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
index eef3bcc9..def921e 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -76,7 +76,6 @@
 #include "third_party/blink/renderer/core/inspector/inspector_resource_content_loader.h"
 #include "third_party/blink/renderer/core/inspector/inspector_session.h"
 #include "third_party/blink/renderer/core/inspector/inspector_task_runner.h"
-#include "third_party/blink/renderer/core/inspector/inspector_tracing_agent.h"
 #include "third_party/blink/renderer/core/inspector/inspector_worker_agent.h"
 #include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -216,7 +215,6 @@
   InspectorSession* inspector_session() { return inspector_session_.Get(); }
   InspectorNetworkAgent* network_agent() { return network_agent_.Get(); }
   InspectorPageAgent* page_agent() { return page_agent_.Get(); }
-  InspectorTracingAgent* tracing_agent() { return tracing_agent_.Get(); }
   InspectorOverlayAgent* overlay_agent() { return overlay_agent_.Get(); }
 
  private:
@@ -252,7 +250,6 @@
   Member<InspectorSession> inspector_session_;
   Member<InspectorNetworkAgent> network_agent_;
   Member<InspectorPageAgent> page_agent_;
-  Member<InspectorTracingAgent> tracing_agent_;
   Member<InspectorOverlayAgent> overlay_agent_;
   bool detached_ = false;
 
@@ -339,7 +336,6 @@
   visitor->Trace(inspector_session_);
   visitor->Trace(network_agent_);
   visitor->Trace(page_agent_);
-  visitor->Trace(tracing_agent_);
   visitor->Trace(overlay_agent_);
 }
 
@@ -449,9 +445,6 @@
   inspector_session_->Append(
       new InspectorWorkerAgent(inspected_frames, nullptr));
 
-  tracing_agent_ = new InspectorTracingAgent(inspected_frames);
-  inspector_session_->Append(tracing_agent_);
-
   page_agent_ = InspectorPageAgent::Create(
       inspected_frames, agent_, agent_->resource_content_loader_.Get(),
       inspector_session_->V8Session());
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 73ed9cb..7c65998 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -403,6 +403,11 @@
   VisualViewport& visual_viewport = GetFrame()->GetPage()->GetVisualViewport();
   EXPECT_FLOAT_SIZE_EQ(FloatSize(320, 240),
                        visual_viewport.ContainerLayer()->Size());
+
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
+    EXPECT_EQ(IntSize(320, 240),
+              visual_viewport.GetScrollNode()->ContainerRect().Size());
+  }
 }
 
 // Make sure that the visibleRect method acurately reflects the scale and scroll
@@ -738,9 +743,14 @@
   // Navigate again, this time the LocalFrameView should be smaller.
   RegisterMockedHttpURLLoad("viewport-device-width.html");
   NavigateTo(base_url_ + "viewport-device-width.html");
+  WebView()->UpdateAllLifecyclePhases();
 
-  // Ensure the scroll layer matches the frame view's size.
+  // Ensure the scroll contents size matches the frame view's size.
   EXPECT_EQ(IntSize(320, 240), visual_viewport.ScrollLayer()->Size());
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
+    EXPECT_EQ(IntSize(320, 240),
+              visual_viewport.GetScrollNode()->ContentsRect().Size());
+  }
 
   // Ensure the location and scale were reset.
   EXPECT_EQ(FloatSize(), visual_viewport.GetScrollOffset());
@@ -1534,6 +1544,15 @@
       frame_view.LayoutViewport()->LayerForScrolling()->CcLayer();
 
   EXPECT_EQ(gfx::Size(1500, 2400), scroll_layer->bounds());
+
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
+    EXPECT_EQ(IntSize(1500, 2400), frame_view.GetLayoutView()
+                                       ->FirstFragment()
+                                       .PaintProperties()
+                                       ->Scroll()
+                                       ->ContentsRect()
+                                       .Size());
+  }
 }
 
 // Tests that resizing the visual viepwort keeps its bounds within the outer
@@ -1957,7 +1976,7 @@
 }
 
 // Make sure a composited background-attachment:fixed background gets resized
-// when using inert (non-layout affecting) browser controls.
+// by browser controls.
 TEST_P(VisualViewportTest, ResizeCompositedAndFixedBackground) {
   if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
     return;
@@ -2033,7 +2052,7 @@
 }
 
 // Make sure a non-composited background-attachment:fixed background gets
-// resized when using inert (non-layout affecting) browser controls.
+// resized by browser controls.
 TEST_P(VisualViewportTest, ResizeNonCompositedAndFixedBackground) {
   if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
     return;
@@ -2342,6 +2361,13 @@
   EXPECT_EQ(gfx::Size(320, 480),
             visual_viewport.ScrollLayer()->CcLayer()->bounds());
 
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
+    EXPECT_EQ(IntSize(400, 600),
+              visual_viewport.GetScrollNode()->ContainerRect().Size());
+    EXPECT_EQ(IntSize(320, 480),
+              visual_viewport.GetScrollNode()->ContentsRect().Size());
+  }
+
   WebView().ApplyViewportDeltas(WebFloatSize(1, 1), WebFloatSize(),
                                 WebFloatSize(), 2, 1);
   EXPECT_EQ(IntSize(400, 600), visual_viewport.ContainerLayer()->Size());
@@ -2350,6 +2376,13 @@
   EXPECT_EQ(IntSize(320, 480), visual_viewport.ScrollLayer()->Size());
   EXPECT_EQ(gfx::Size(320, 480),
             visual_viewport.ScrollLayer()->CcLayer()->bounds());
+
+  if (RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
+    EXPECT_EQ(IntSize(400, 600),
+              visual_viewport.GetScrollNode()->ContainerRect().Size());
+    EXPECT_EQ(IntSize(320, 480),
+              visual_viewport.GetScrollNode()->ContentsRect().Size());
+  }
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index d155b06..418475e 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -879,7 +879,16 @@
       View()->SetLastHiddenPagePopup(nullptr);
       break;
     case WebInputEvent::kGestureShowPress:
+      break;
     case WebInputEvent::kGestureDoubleTap:
+      // Until https://crbug.com/734209 is resolved and OOPIFs learn how to
+      // handle AnimateDoubleTap, we shouldn't pass this event to the event
+      // handler as it will just result in (at best) hitting NOTREACHED() in
+      // debug builds.
+      LOG(INFO) << "DoubleTap zoom animations not yet implemented for OOPIF.";
+      event_result = WebInputEventResult::kHandledSystem;
+      Client()->DidHandleGestureEvent(event, event_cancelled);
+      return event_result;
       break;
     case WebInputEvent::kGestureTwoFingerTap:
     case WebInputEvent::kGestureLongPress:
diff --git a/third_party/blink/renderer/core/inspector/BUILD.gn b/third_party/blink/renderer/core/inspector/BUILD.gn
index 414d1ed..862d1eb 100644
--- a/third_party/blink/renderer/core/inspector/BUILD.gn
+++ b/third_party/blink/renderer/core/inspector/BUILD.gn
@@ -85,8 +85,6 @@
     "inspector_task_runner.h",
     "inspector_trace_events.cc",
     "inspector_trace_events.h",
-    "inspector_tracing_agent.cc",
-    "inspector_tracing_agent.h",
     "inspector_worker_agent.cc",
     "inspector_worker_agent.h",
     "main_thread_debugger.cc",
@@ -177,8 +175,6 @@
     "inspector/protocol/Security.h",
     "inspector/protocol/Target.cpp",
     "inspector/protocol/Target.h",
-    "inspector/protocol/Tracing.cpp",
-    "inspector/protocol/Tracing.h",
   ]
 
   deps = [
diff --git a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
index 6c1a9dd..b754fdc 100644
--- a/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
+++ b/third_party/blink/renderer/core/inspector/inspector_protocol_config.json
@@ -72,11 +72,6 @@
                 "include": ["getDOMCounters", "startSampling", "stopSampling", "getSamplingProfile", "getAllTimeSamplingProfile"]
             },
             {
-                "domain": "Tracing",
-                "include": ["start", "end"],
-                "async": ["start", "end"]
-            },
-            {
                 "domain": "Page",
                 "exclude": ["getNavigationHistory", "navigateToHistoryEntry", "captureScreenshot", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled",
                             "getAppManifest", "requestAppBanner", "setControlNavigations", "processNavigation", "printToPDF", "bringToFront", "setDownloadBehavior", "navigate", "crash", "close", "setWebLifecycleState"],
diff --git a/third_party/blink/renderer/core/inspector/inspector_tracing_agent.cc b/third_party/blink/renderer/core/inspector/inspector_tracing_agent.cc
deleted file mode 100644
index 36105bd..0000000
--- a/third_party/blink/renderer/core/inspector/inspector_tracing_agent.cc
+++ /dev/null
@@ -1,92 +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 "third_party/blink/renderer/core/inspector/inspector_tracing_agent.h"
-
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
-#include "third_party/blink/renderer/core/inspector/inspected_frames.h"
-#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
-#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-
-namespace blink {
-
-using protocol::Maybe;
-using protocol::Response;
-
-namespace {
-const char kDevtoolsMetadataEventCategory[] =
-    TRACE_DISABLED_BY_DEFAULT("devtools.timeline");
-}
-
-InspectorTracingAgent::InspectorTracingAgent(InspectedFrames* inspected_frames)
-    : session_id_(&agent_state_, /*default_value=*/ WTF::String()),
-      inspected_frames_(inspected_frames) {}
-
-InspectorTracingAgent::~InspectorTracingAgent() {}
-
-void InspectorTracingAgent::Trace(blink::Visitor* visitor) {
-  visitor->Trace(inspected_frames_);
-  InspectorBaseAgent::Trace(visitor);
-}
-
-void InspectorTracingAgent::Restore() {
-  if (IsStarted())
-    EmitMetadataEvents();
-}
-
-void InspectorTracingAgent::start(Maybe<String> categories,
-                                  Maybe<String> options,
-                                  Maybe<double> buffer_usage_reporting_interval,
-                                  Maybe<String> transfer_mode,
-                                  Maybe<String> transfer_compression,
-                                  Maybe<protocol::Tracing::TraceConfig> config,
-                                  std::unique_ptr<StartCallback> callback) {
-  DCHECK(!IsStarted());
-  if (config.isJust()) {
-    callback->sendFailure(Response::Error(
-        "Using trace config on renderer targets is not supported yet."));
-    return;
-  }
-
-  session_id_.Set(IdentifiersFactory::CreateIdentifier());
-
-  // Tracing is already started by DevTools TracingHandler::Start for the
-  // renderer target in the browser process. It will eventually start tracing
-  // in the renderer process via IPC. But we still need a redundant enable here
-  // for EmitMetadataEvents, at which point we are not sure if tracing
-  // is already started in the renderer process.
-  TraceEvent::EnableTracing(categories.fromMaybe(String()));
-
-  EmitMetadataEvents();
-  callback->sendSuccess();
-}
-
-void InspectorTracingAgent::end(std::unique_ptr<EndCallback> callback) {
-  TraceEvent::DisableTracing();
-  InnerDisable();
-  callback->sendSuccess();
-}
-
-bool InspectorTracingAgent::IsStarted() const {
-  return !session_id_.Get().IsEmpty();
-}
-
-void InspectorTracingAgent::EmitMetadataEvents() {
-  TRACE_EVENT_INSTANT1(kDevtoolsMetadataEventCategory, "TracingStartedInPage",
-                       TRACE_EVENT_SCOPE_THREAD, "data",
-                       InspectorTracingStartedInFrame::Data(
-                           session_id_.Get(), inspected_frames_->Root()));
-}
-
-Response InspectorTracingAgent::disable() {
-  InnerDisable();
-  return Response::OK();
-}
-
-void InspectorTracingAgent::InnerDisable() {
-  session_id_.Clear();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_tracing_agent.h b/third_party/blink/renderer/core/inspector/inspector_tracing_agent.h
deleted file mode 100644
index 0d02c4f..0000000
--- a/third_party/blink/renderer/core/inspector/inspector_tracing_agent.h
+++ /dev/null
@@ -1,55 +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 THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_TRACING_AGENT_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_TRACING_AGENT_H_
-
-#include "base/macros.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/inspector/inspector_base_agent.h"
-#include "third_party/blink/renderer/core/inspector/protocol/Tracing.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-class InspectedFrames;
-
-class CORE_EXPORT InspectorTracingAgent final
-    : public InspectorBaseAgent<protocol::Tracing::Metainfo> {
- public:
-  explicit InspectorTracingAgent(InspectedFrames*);
-  ~InspectorTracingAgent() override;
-
-  void Trace(blink::Visitor*) override;
-
-  // Base agent methods.
-  void Restore() override;
-  protocol::Response disable() override;
-
-  // Protocol method implementations.
-  void start(protocol::Maybe<String> categories,
-             protocol::Maybe<String> options,
-             protocol::Maybe<double> buffer_usage_reporting_interval,
-             protocol::Maybe<String> transfer_mode,
-             protocol::Maybe<String> transfer_compression,
-             protocol::Maybe<protocol::Tracing::TraceConfig>,
-             std::unique_ptr<StartCallback>) override;
-  void end(std::unique_ptr<EndCallback>) override;
-
- private:
-  void EmitMetadataEvents();
-  void InnerDisable();
-  bool IsStarted() const;
-
-  InspectorAgentState::String session_id_;
-  Member<InspectedFrames> inspected_frames_;
-
-  DISALLOW_COPY_AND_ASSIGN(InspectorTracingAgent);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_TRACING_AGENT_H_
diff --git a/third_party/blink/renderer/core/layout/layout_file_upload_control.cc b/third_party/blink/renderer/core/layout/layout_file_upload_control.cc
index 3624f93..ac4ec435 100644
--- a/third_party/blink/renderer/core/layout/layout_file_upload_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_file_upload_control.cc
@@ -38,6 +38,7 @@
 using namespace HTMLNames;
 
 const int kDefaultWidthNumChars = 34;
+const int kButtonShadowHeight = 2;
 
 LayoutFileUploadControl::LayoutFileUploadControl(HTMLInputElement* input)
     : LayoutBlockFlow(input),
@@ -181,4 +182,20 @@
       MaxFilenameWidth());
 }
 
+LayoutRect LayoutFileUploadControl::ControlClipRect(
+    const LayoutPoint& additional_offset) const {
+  LayoutRect rect(additional_offset, Size());
+  rect.Expand(BorderInsets());
+  rect.Expand(LayoutUnit(), LayoutUnit(kButtonShadowHeight));
+  return rect;
+}
+
+// Override to allow effective ControlClipRect to be bigger than the padding
+// box because of kButtonShadowHeight.
+LayoutRect LayoutFileUploadControl::OverflowClipRect(
+    const LayoutPoint& additional_offset,
+    OverlayScrollbarClipBehavior) const {
+  return ControlClipRect(additional_offset);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_file_upload_control.h b/third_party/blink/renderer/core/layout/layout_file_upload_control.h
index 4c4d2cd..3e84094 100644
--- a/third_party/blink/renderer/core/layout/layout_file_upload_control.h
+++ b/third_party/blink/renderer/core/layout/layout_file_upload_control.h
@@ -49,6 +49,11 @@
   HTMLInputElement* UploadButton() const;
   int UploadButtonWidth();
 
+  bool HasControlClip() const override { return true; }
+  LayoutRect ControlClipRect(const LayoutPoint&) const override;
+  LayoutRect OverflowClipRect(const LayoutPoint&,
+                              OverlayScrollbarClipBehavior) const override;
+
   static const int kAfterButtonSpacing = 4;
 
   const char* GetName() const override { return "LayoutFileUploadControl"; }
diff --git a/third_party/blink/renderer/core/layout/layout_geometry_map_step.h b/third_party/blink/renderer/core/layout/layout_geometry_map_step.h
index 280b3f5e..e56ea11e 100644
--- a/third_party/blink/renderer/core/layout/layout_geometry_map_step.h
+++ b/third_party/blink/renderer/core/layout/layout_geometry_map_step.h
@@ -53,7 +53,6 @@
       : layout_object_(o.layout_object_),
         offset_(o.offset_),
         offset_for_fixed_position_(o.offset_for_fixed_position_),
-        offset_for_sticky_position_(o.offset_for_sticky_position_),
         flags_(o.flags_) {
     DCHECK(!o.transform_);
   }
@@ -64,11 +63,7 @@
   LayoutSize offset_;
   std::unique_ptr<TransformationMatrix>
       transform_;  // Includes offset if non-null.
-  // If m_offsetForFixedPosition could only apply to the fixed position steps,
-  // we may be able to merge with m_offsetForStickyPosition and simplify
-  // mapping.
   LayoutSize offset_for_fixed_position_;
-  LayoutSize offset_for_sticky_position_;
   GeometryInfoFlags flags_;
 };
 
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.h b/third_party/blink/renderer/core/messaging/blink_transferable_message.h
index 08e92a7..b1ac191 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.h
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/blink_cloneable_message.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
 
 namespace blink {
 
@@ -44,6 +45,15 @@
 // longer.
 CORE_EXPORT TransferableMessage ToTransferableMessage(BlinkTransferableMessage);
 
+template <>
+struct CrossThreadCopier<BlinkTransferableMessage> {
+  STATIC_ONLY(CrossThreadCopier);
+  using Type = BlinkTransferableMessage;
+  static Type Copy(Type pointer) {
+    return pointer;  // This is in fact a move.
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_MESSAGING_BLINK_TRANSFERABLE_MESSAGE_H_
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index 90fedaa..32636cc 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -26,12 +26,14 @@
 
 #include "third_party/blink/renderer/core/page/create_window.h"
 
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/mojom/request_context_frame_type.mojom-blink.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/web/web_window_features.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
+#include "third_party/blink/renderer/core/frame/ad_tracker.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -195,6 +197,24 @@
   return nullptr;
 }
 
+static void MaybeLogWindowOpenUKM(LocalFrame& opener_frame) {
+  AdTracker* ad_tracker = opener_frame.GetAdTracker();
+  if (!ad_tracker) {
+    return;
+  }
+
+  ukm::UkmRecorder* ukm_recorder = opener_frame.GetDocument()->UkmRecorder();
+  ukm::SourceId source_id = opener_frame.GetDocument()->UkmSourceID();
+  bool is_ad_subframe = opener_frame.IsAdSubframe();
+  bool is_ad_script_in_stack = ad_tracker->IsAdScriptInStack();
+  if (source_id != ukm::kInvalidSourceId) {
+    ukm::builders::AbusiveExperienceHeuristic(source_id)
+        .SetDidWindowOpenFromAdSubframe(is_ad_subframe)
+        .SetDidWindowOpenFromAdScript(is_ad_script_in_stack)
+        .Record(ukm_recorder);
+  }
+}
+
 static Frame* CreateNewWindow(LocalFrame& opener_frame,
                               const FrameLoadRequest& request,
                               const WebWindowFeatures& features,
@@ -256,6 +276,7 @@
   page->GetChromeClient().SetWindowRectWithAdjustment(window_rect, frame);
   page->GetChromeClient().Show(policy);
 
+  MaybeLogWindowOpenUKM(opener_frame);
   created = true;
   return &frame;
 }
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index ad41119..5117044 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -243,12 +243,15 @@
   if (object.IsSVGRoot())
     return true;
 
-  // Non-composited images have a micro-optimization to embed object-fit into
-  // the drawings instead of using a transform node.
+  // Only directly composited images need a transform node to scale contents
+  // to the object-fit box. Note that we don't actually know whether the image
+  // will be directly composited. This condition is relaxed to stay on the
+  // safe side.
+  // TODO(crbug.com/875110): Figure out the condition for SPv2.
   bool is_spv1_composited =
       object.HasLayer() &&
       ToLayoutBoxModelObject(object).Layer()->GetCompositedLayerMapping();
-  if (object.IsLayoutImage() && !object.IsVideo() && is_spv1_composited)
+  if (object.IsImage() && is_spv1_composited)
     return true;
 
   return false;
@@ -1012,7 +1015,7 @@
   // the drawings instead of using a clip node.
   bool is_spv1_composited =
       replaced.HasLayer() && replaced.Layer()->GetCompositedLayerMapping();
-  if (replaced.IsLayoutImage() && !replaced.IsVideo() && !is_spv1_composited)
+  if (replaced.IsImage() && !is_spv1_composited)
     return false;
 
   // Embedded objects are always sized to fit the content rect.
@@ -1298,7 +1301,7 @@
       content_to_parent_space =
           SVGRootPainter(ToLayoutSVGRoot(object_))
               .TransformToPixelSnappedBorderBox(context_.current.paint_offset);
-    } else if (object_.IsLayoutImage() && !object_.IsVideo()) {
+    } else if (object_.IsImage()) {
       const LayoutImage& layout_image = ToLayoutImage(object_);
       LayoutRect layout_replaced_rect = layout_image.ReplacedContentRect();
       layout_replaced_rect.MoveBy(context_.current.paint_offset);
@@ -1312,6 +1315,8 @@
         content_to_parent_space =
             RectToRect(FloatRect(src_rect), FloatRect(replaced_rect));
       }
+    } else {
+      NOTREACHED();
     }
     if (!content_to_parent_space.IsIdentity()) {
       OnUpdate(properties_->UpdateReplacedContentTransform(
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc
index 07ddd7b..4f5ac757 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.cc
+++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -92,11 +92,9 @@
         PropertyTreeState new_properties =
             local_paint_info.context.GetPaintController()
                 .CurrentPaintChunkProperties();
-        bool painter_implements_object_fit_and_clip =
-            layout_replaced_.IsLayoutImage();
         bool property_changed = false;
         if (paint_properties->ReplacedContentTransform() &&
-            !painter_implements_object_fit_and_clip) {
+            layout_replaced_.IsSVGRoot()) {
           new_properties.SetTransform(
               paint_properties->ReplacedContentTransform());
           DCHECK(paint_properties->ReplacedContentTransform()
@@ -108,8 +106,10 @@
                   .ToAffineTransform());
           property_changed = true;
         }
+        bool painter_implements_content_box_clip =
+            layout_replaced_.IsLayoutImage();
         if (paint_properties->OverflowClip() &&
-            (!painter_implements_object_fit_and_clip ||
+            (!painter_implements_content_box_clip ||
              layout_replaced_.StyleRef().HasBorderRadius())) {
           new_properties.SetClip(paint_properties->OverflowClip());
           property_changed = true;
diff --git a/third_party/blink/renderer/core/svg/properties/svg_animated_property.h b/third_party/blink/renderer/core/svg/properties/svg_animated_property.h
index cebc35b..349b8b80 100644
--- a/third_party/blink/renderer/core/svg/properties/svg_animated_property.h
+++ b/third_party/blink/renderer/core/svg/properties/svg_animated_property.h
@@ -57,7 +57,7 @@
   virtual void SetAnimatedValue(SVGPropertyBase*) = 0;
   virtual void AnimationEnded();
 
-  virtual SVGParsingError SetBaseValueAsString(const String&) = 0;
+  virtual SVGParsingError AttributeChanged(const String&) = 0;
   virtual bool NeedsSynchronizeAttribute() = 0;
   virtual void SynchronizeAttribute();
 
@@ -123,7 +123,7 @@
 
   bool IsAnimating() const override { return current_value_; }
 
-  SVGParsingError SetBaseValueAsString(const String& value) override {
+  SVGParsingError AttributeChanged(const String& value) override {
     return base_value_->SetValueAsString(value);
   }
 
diff --git a/third_party/blink/renderer/core/svg/svg_animated_length.cc b/third_party/blink/renderer/core/svg/svg_animated_length.cc
index b44a605e..e1d5587 100644
--- a/third_party/blink/renderer/core/svg/svg_animated_length.cc
+++ b/third_party/blink/renderer/core/svg/svg_animated_length.cc
@@ -38,8 +38,9 @@
   BaseValue()->SetValueAsString(value);
 }
 
-SVGParsingError SVGAnimatedLength::SetBaseValueAsString(const String& value) {
-  SVGParsingError parse_status = BaseValue()->SetValueAsString(value);
+SVGParsingError SVGAnimatedLength::AttributeChanged(const String& value) {
+  SVGParsingError parse_status =
+      SVGAnimatedProperty<SVGLength>::AttributeChanged(value);
 
   if (parse_status != SVGParseStatus::kNoError)
     BaseValue()->NewValueSpecifiedUnits(CSSPrimitiveValue::UnitType::kUserUnits,
diff --git a/third_party/blink/renderer/core/svg/svg_animated_length.h b/third_party/blink/renderer/core/svg/svg_animated_length.h
index eab3f9780..9384ee5 100644
--- a/third_party/blink/renderer/core/svg/svg_animated_length.h
+++ b/third_party/blink/renderer/core/svg/svg_animated_length.h
@@ -53,7 +53,7 @@
   }
 
   void SetDefaultValueAsString(const String&);
-  SVGParsingError SetBaseValueAsString(const String&) override;
+  SVGParsingError AttributeChanged(const String&) override;
 
   const CSSValue& CssValue() const {
     return CurrentValue()->AsCSSPrimitiveValue();
diff --git a/third_party/blink/renderer/core/svg/svg_element.cc b/third_party/blink/renderer/core/svg/svg_element.cc
index 08f653cb..8f49111f 100644
--- a/third_party/blink/renderer/core/svg/svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_element.cc
@@ -679,8 +679,7 @@
   // Element::ParseAttribute). We don't tell Element about the change to avoid
   // parsing the class list twice.
   if (SVGAnimatedPropertyBase* property = PropertyFromAttribute(params.name)) {
-    SVGParsingError parse_error =
-        property->SetBaseValueAsString(params.new_value);
+    SVGParsingError parse_error = property->AttributeChanged(params.new_value);
     ReportAttributeParsingError(parse_error, params.name, params.new_value);
     return;
   }
diff --git a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc
index 63170c4..0536c88 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.cc
@@ -44,7 +44,7 @@
     return new SVGAnimatedOrder(context_element);
   }
 
-  SVGParsingError SetBaseValueAsString(const String&) override;
+  SVGParsingError AttributeChanged(const String&) override;
 
  protected:
   SVGAnimatedOrder(SVGElement* context_element)
@@ -64,9 +64,9 @@
   }
 };
 
-SVGParsingError SVGAnimatedOrder::SetBaseValueAsString(const String& value) {
+SVGParsingError SVGAnimatedOrder::AttributeChanged(const String& value) {
   SVGParsingError parse_status =
-      SVGAnimatedIntegerOptionalInteger::SetBaseValueAsString(value);
+      SVGAnimatedIntegerOptionalInteger::AttributeChanged(value);
   // Check for semantic errors.
   parse_status = CheckValue(parse_status, FirstInteger()->BaseValue()->Value());
   parse_status =
diff --git a/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc b/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc
index 7328b6e..4fab9310 100644
--- a/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc
+++ b/third_party/blink/renderer/core/svg/svg_fit_to_view_box.cc
@@ -35,16 +35,15 @@
     return new SVGAnimatedViewBoxRect(context_element);
   }
 
-  SVGParsingError SetBaseValueAsString(const String&) override;
+  SVGParsingError AttributeChanged(const String&) override;
 
  protected:
   SVGAnimatedViewBoxRect(SVGElement* context_element)
       : SVGAnimatedRect(context_element, SVGNames::viewBoxAttr) {}
 };
 
-SVGParsingError SVGAnimatedViewBoxRect::SetBaseValueAsString(
-    const String& value) {
-  SVGParsingError parse_status = SVGAnimatedRect::SetBaseValueAsString(value);
+SVGParsingError SVGAnimatedViewBoxRect::AttributeChanged(const String& value) {
+  SVGParsingError parse_status = SVGAnimatedRect::AttributeChanged(value);
 
   if (parse_status == SVGParseStatus::kNoError &&
       (BaseValue()->Width() < 0 || BaseValue()->Height() < 0)) {
diff --git a/third_party/blink/renderer/core/svg/svg_geometry_element.cc b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
index 97ee5aae..7096142 100644
--- a/third_party/blink/renderer/core/svg/svg_geometry_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
@@ -46,9 +46,8 @@
     return new SVGAnimatedPathLength(context_element);
   }
 
-  SVGParsingError SetBaseValueAsString(const String& value) override {
-    SVGParsingError parse_status =
-        SVGAnimatedNumber::SetBaseValueAsString(value);
+  SVGParsingError AttributeChanged(const String& value) override {
+    SVGParsingError parse_status = SVGAnimatedNumber::AttributeChanged(value);
     if (parse_status == SVGParseStatus::kNoError && BaseValue()->Value() < 0)
       parse_status = SVGParseStatus::kNegativeValue;
     return parse_status;
diff --git a/third_party/blink/renderer/core/svg/svg_static_string_list.cc b/third_party/blink/renderer/core/svg/svg_static_string_list.cc
index 1e52b9b..cd72912 100644
--- a/third_party/blink/renderer/core/svg/svg_static_string_list.cc
+++ b/third_party/blink/renderer/core/svg/svg_static_string_list.cc
@@ -89,7 +89,7 @@
   return tear_off_.Get();
 }
 
-SVGParsingError SVGStaticStringList::SetBaseValueAsString(const String& value) {
+SVGParsingError SVGStaticStringList::AttributeChanged(const String& value) {
   return value_->SetValueAsString(value);
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_static_string_list.h b/third_party/blink/renderer/core/svg/svg_static_string_list.h
index 2298ebe0f..b37f591 100644
--- a/third_party/blink/renderer/core/svg/svg_static_string_list.h
+++ b/third_party/blink/renderer/core/svg/svg_static_string_list.h
@@ -64,7 +64,7 @@
   void AnimationEnded() override;
   bool NeedsSynchronizeAttribute() override;
 
-  SVGParsingError SetBaseValueAsString(const String&) override;
+  SVGParsingError AttributeChanged(const String&) override;
 
   SVGStringList* Value() { return value_.Get(); }
   SVGStringListTearOff* TearOff();
diff --git a/third_party/blink/renderer/core/svg/svg_svg_element.cc b/third_party/blink/renderer/core/svg/svg_svg_element.cc
index bf00d67..7a43561 100644
--- a/third_party/blink/renderer/core/svg/svg_svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_svg_element.cc
@@ -199,7 +199,7 @@
         name == SVGNames::widthAttr ? width_ : height_;
     SVGParsingError parse_error;
     if (!value.IsNull())
-      parse_error = property->SetBaseValueAsString(value);
+      parse_error = property->AttributeChanged(value);
     if (parse_error != SVGParseStatus::kNoError || value.IsNull())
       property->SetDefaultValueAsString("100%");
     ReportAttributeParsingError(parse_error, name, value);
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
index d417e0d..38368e7e 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
@@ -135,6 +135,7 @@
                                   ExceptionState& exception_state) {
   DCHECK(GetExecutionContext()->IsContextThread());
 
+  BlinkTransferableMessage transferable_message;
   Transferables transferables;
   scoped_refptr<SerializedScriptValue> serialized_message =
       PostMessageHelper::SerializeMessageByMove(script_state->GetIsolate(),
@@ -143,18 +144,20 @@
   if (exception_state.HadException())
     return;
   DCHECK(serialized_message);
+  transferable_message.message = serialized_message;
 
   // Disentangle the port in preparation for sending it to the remote context.
-  auto channels = MessagePort::DisentanglePorts(
+  transferable_message.ports = MessagePort::DisentanglePorts(
       ExecutionContext::From(script_state), transferables.message_ports,
       exception_state);
   if (exception_state.HadException())
     return;
-  v8_inspector::V8StackTraceId stack_id =
+
+  transferable_message.sender_stack_trace_id =
       ThreadDebugger::From(script_state->GetIsolate())
           ->StoreCurrentStackTrace("Worker.postMessage");
-  context_proxy_->PostMessageToWorkerGlobalScope(std::move(serialized_message),
-                                                 std::move(channels), stack_id);
+  context_proxy_->PostMessageToWorkerGlobalScope(
+      std::move(transferable_message));
 }
 
 // https://html.spec.whatwg.org/multipage/workers.html#worker-processing-model
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
index 07d699bd..2831cfa0 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.cc
@@ -106,17 +106,19 @@
   if (exception_state.HadException())
     return;
   DCHECK(serialized_message);
+  BlinkTransferableMessage transferable_message;
+  transferable_message.message = serialized_message;
   // Disentangle the port in preparation for sending it to the remote context.
-  auto channels = MessagePort::DisentanglePorts(
+  transferable_message.ports = MessagePort::DisentanglePorts(
       ExecutionContext::From(script_state), transferables.message_ports,
       exception_state);
   if (exception_state.HadException())
     return;
   ThreadDebugger* debugger = ThreadDebugger::From(script_state->GetIsolate());
-  v8_inspector::V8StackTraceId stack_id =
+  transferable_message.sender_stack_trace_id =
       debugger->StoreCurrentStackTrace("postMessage");
-  WorkerObjectProxy().PostMessageToWorkerObject(std::move(serialized_message),
-                                                std::move(channels), stack_id);
+  WorkerObjectProxy().PostMessageToWorkerObject(
+      std::move(transferable_message));
 }
 
 DedicatedWorkerObjectProxy& DedicatedWorkerGlobalScope::WorkerObjectProxy()
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
index 12221bb..939bd22 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/fetch/request.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/inspector/thread_debugger.h"
+#include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_thread.h"
@@ -25,12 +26,6 @@
 
 namespace blink {
 
-struct DedicatedWorkerMessagingProxy::QueuedTask {
-  scoped_refptr<SerializedScriptValue> message;
-  Vector<MessagePortChannel> channels;
-  v8_inspector::V8StackTraceId stack_id;
-};
-
 DedicatedWorkerMessagingProxy::DedicatedWorkerMessagingProxy(
     ExecutionContext* execution_context,
     DedicatedWorker* worker_object)
@@ -76,24 +71,21 @@
 }
 
 void DedicatedWorkerMessagingProxy::PostMessageToWorkerGlobalScope(
-    scoped_refptr<SerializedScriptValue> message,
-    Vector<MessagePortChannel> channels,
-    const v8_inspector::V8StackTraceId& stack_id) {
+    BlinkTransferableMessage message) {
   DCHECK(IsParentContextThread());
   if (AskedToTerminate())
     return;
   if (!was_script_evaluated_) {
-    queued_early_tasks_.push_back(
-        QueuedTask{std::move(message), std::move(channels), stack_id});
+    queued_early_tasks_.push_back(std::move(message));
     return;
   }
   PostCrossThreadTask(
       *GetWorkerThread()->GetTaskRunner(TaskType::kPostedMessage), FROM_HERE,
       CrossThreadBind(
           &DedicatedWorkerObjectProxy::ProcessMessageFromWorkerObject,
-          CrossThreadUnretained(&WorkerObjectProxy()), std::move(message),
-          WTF::Passed(std::move(channels)),
-          CrossThreadUnretained(GetWorkerThread()), stack_id));
+          CrossThreadUnretained(&WorkerObjectProxy()),
+          WTF::Passed(std::move(message)),
+          CrossThreadUnretained(GetWorkerThread())));
 }
 
 bool DedicatedWorkerMessagingProxy::HasPendingActivity() const {
@@ -105,7 +97,7 @@
   DCHECK(IsParentContextThread());
   was_script_evaluated_ = true;
 
-  Vector<QueuedTask> tasks;
+  Vector<BlinkTransferableMessage> tasks;
   queued_early_tasks_.swap(tasks);
 
   // The worker thread can already be terminated.
@@ -123,28 +115,25 @@
         CrossThreadBind(
             &DedicatedWorkerObjectProxy::ProcessMessageFromWorkerObject,
             CrossThreadUnretained(&WorkerObjectProxy()),
-            WTF::Passed(std::move(task.message)),
-            WTF::Passed(std::move(task.channels)),
-            CrossThreadUnretained(GetWorkerThread()), task.stack_id));
+            WTF::Passed(std::move(task)),
+            CrossThreadUnretained(GetWorkerThread())));
   }
 }
 
 void DedicatedWorkerMessagingProxy::PostMessageToWorkerObject(
-    scoped_refptr<SerializedScriptValue> message,
-    Vector<MessagePortChannel> channels,
-    const v8_inspector::V8StackTraceId& stack_id) {
+    BlinkTransferableMessage message) {
   DCHECK(IsParentContextThread());
   if (!worker_object_ || AskedToTerminate())
     return;
 
   ThreadDebugger* debugger =
       ThreadDebugger::From(ToIsolate(GetExecutionContext()));
-  MessagePortArray* ports =
-      MessagePort::EntanglePorts(*GetExecutionContext(), std::move(channels));
-  debugger->ExternalAsyncTaskStarted(stack_id);
+  MessagePortArray* ports = MessagePort::EntanglePorts(
+      *GetExecutionContext(), std::move(message.ports));
+  debugger->ExternalAsyncTaskStarted(message.sender_stack_trace_id);
   worker_object_->DispatchEvent(
-      *MessageEvent::Create(ports, std::move(message)));
-  debugger->ExternalAsyncTaskFinished(stack_id);
+      *MessageEvent::Create(ports, std::move(message.message)));
+  debugger->ExternalAsyncTaskFinished(message.sender_stack_trace_id);
 }
 
 void DedicatedWorkerMessagingProxy::DispatchErrorEvent(
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
index 7fcb3fd..12fbe5e 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
+#include "third_party/blink/public/mojom/message_port/message_port.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
@@ -17,16 +18,11 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/weborigin/referrer_policy.h"
 
-namespace v8_inspector {
-struct V8StackTraceId;
-}
-
 namespace blink {
 
 class DedicatedWorker;
 class DedicatedWorkerObjectProxy;
 class FetchClientSettingsObjectSnapshot;
-class SerializedScriptValue;
 class WorkerOptions;
 
 // A proxy class to talk to the DedicatedWorkerGlobalScope on a worker thread
@@ -46,18 +42,14 @@
       FetchClientSettingsObjectSnapshot* outside_settings_object,
       const v8_inspector::V8StackTraceId&,
       const String& source_code);
-  void PostMessageToWorkerGlobalScope(scoped_refptr<SerializedScriptValue>,
-                                      Vector<MessagePortChannel>,
-                                      const v8_inspector::V8StackTraceId&);
+  void PostMessageToWorkerGlobalScope(BlinkTransferableMessage);
 
   bool HasPendingActivity() const;
 
   // These methods come from worker context thread via
   // DedicatedWorkerObjectProxy and are called on the parent context thread.
   void DidEvaluateScript(bool success);
-  void PostMessageToWorkerObject(scoped_refptr<SerializedScriptValue>,
-                                 Vector<MessagePortChannel>,
-                                 const v8_inspector::V8StackTraceId&);
+  void PostMessageToWorkerObject(BlinkTransferableMessage);
   void DispatchErrorEvent(const String& error_message,
                           std::unique_ptr<SourceLocation>,
                           int exception_id);
@@ -92,8 +84,7 @@
 
   // Tasks are queued here until worker scripts are evaluated on the worker
   // global scope.
-  struct QueuedTask;
-  Vector<QueuedTask> queued_early_tasks_;
+  Vector<BlinkTransferableMessage> queued_early_tasks_;
   DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerMessagingProxy);
 };
 
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc
index 22f92a5..6a00ee12 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/user_activation.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/thread_debugger.h"
 #include "third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h"
@@ -64,31 +65,28 @@
 DedicatedWorkerObjectProxy::~DedicatedWorkerObjectProxy() = default;
 
 void DedicatedWorkerObjectProxy::PostMessageToWorkerObject(
-    scoped_refptr<SerializedScriptValue> message,
-    Vector<MessagePortChannel> channels,
-    const v8_inspector::V8StackTraceId& stack_id) {
+    BlinkTransferableMessage message) {
   PostCrossThreadTask(
       *GetParentExecutionContextTaskRunners()->Get(TaskType::kPostedMessage),
       FROM_HERE,
       CrossThreadBind(&DedicatedWorkerMessagingProxy::PostMessageToWorkerObject,
-                      messaging_proxy_weak_ptr_, std::move(message),
-                      WTF::Passed(std::move(channels)), stack_id));
+                      messaging_proxy_weak_ptr_,
+                      WTF::Passed(std::move(message))));
 }
 
 void DedicatedWorkerObjectProxy::ProcessMessageFromWorkerObject(
-    scoped_refptr<SerializedScriptValue> message,
-    Vector<MessagePortChannel> channels,
-    WorkerThread* worker_thread,
-    const v8_inspector::V8StackTraceId& stack_id) {
+    BlinkTransferableMessage message,
+    WorkerThread* worker_thread) {
   WorkerGlobalScope* global_scope =
       ToWorkerGlobalScope(worker_thread->GlobalScope());
   MessagePortArray* ports =
-      MessagePort::EntanglePorts(*global_scope, std::move(channels));
+      MessagePort::EntanglePorts(*global_scope, std::move(message.ports));
 
   ThreadDebugger* debugger = ThreadDebugger::From(worker_thread->GetIsolate());
-  debugger->ExternalAsyncTaskStarted(stack_id);
-  global_scope->DispatchEvent(*MessageEvent::Create(ports, std::move(message)));
-  debugger->ExternalAsyncTaskFinished(stack_id);
+  debugger->ExternalAsyncTaskStarted(message.sender_stack_trace_id);
+  global_scope->DispatchEvent(
+      *MessageEvent::Create(ports, std::move(message.message)));
+  debugger->ExternalAsyncTaskFinished(message.sender_stack_trace_id);
 }
 
 void DedicatedWorkerObjectProxy::ProcessUnhandledException(
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
index 319def7..3d78c12 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
@@ -35,15 +35,12 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/workers/threaded_object_proxy_base.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
-namespace v8_inspector {
-struct V8StackTraceId;
-}  // namespace v8_inspector
-
 namespace blink {
 
 class DedicatedWorkerMessagingProxy;
@@ -63,14 +60,9 @@
       ParentExecutionContextTaskRunners*);
   ~DedicatedWorkerObjectProxy() override;
 
-  void PostMessageToWorkerObject(scoped_refptr<SerializedScriptValue>,
-                                 Vector<MessagePortChannel>,
-                                 const v8_inspector::V8StackTraceId&);
+  void PostMessageToWorkerObject(BlinkTransferableMessage);
   void ProcessUnhandledException(int exception_id, WorkerThread*);
-  void ProcessMessageFromWorkerObject(scoped_refptr<SerializedScriptValue>,
-                                      Vector<MessagePortChannel>,
-                                      WorkerThread*,
-                                      const v8_inspector::V8StackTraceId&);
+  void ProcessMessageFromWorkerObject(BlinkTransferableMessage, WorkerThread*);
 
   // ThreadedObjectProxyBase overrides.
   void ReportException(const String& error_message,
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
index 33ca87c..49f9a65 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_test.cc
@@ -177,9 +177,8 @@
   }
 
   void DispatchMessageEvent() {
-    WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(
-        nullptr /* message */, Vector<MessagePortChannel>(),
-        v8_inspector::V8StackTraceId());
+    BlinkTransferableMessage message;
+    WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
   }
 
   DedicatedWorkerMessagingProxyForTest* WorkerMessagingProxy() {
diff --git a/third_party/blink/renderer/core/workers/worker_backing_thread.cc b/third_party/blink/renderer/core/workers/worker_backing_thread.cc
index 21d16b4c..ade0105 100644
--- a/third_party/blink/renderer/core/workers/worker_backing_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_backing_thread.cc
@@ -32,7 +32,7 @@
 
 HashSet<v8::Isolate*>& Isolates() {
 #if DCHECK_IS_ON()
-  DCHECK(IsolatesMutex().Locked());
+  IsolatesMutex().AssertAcquired();
 #endif
   static HashSet<v8::Isolate*>& isolates = *new HashSet<v8::Isolate*>();
   return isolates;
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index 7cbe6c5..172204d7 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -48,7 +48,6 @@
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
diff --git a/third_party/blink/renderer/devtools/front_end/cm/activeline.js b/third_party/blink/renderer/devtools/front_end/cm/activeline.js
index aa295d0..c7b14ce 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/activeline.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/activeline.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/third_party/blink/renderer/devtools/front_end/cm/closebrackets.js b/third_party/blink/renderer/devtools/front_end/cm/closebrackets.js
index 460f662f..ce1a4ac 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/closebrackets.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/closebrackets.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -129,16 +129,14 @@
         else
           curType = "skip";
       } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
-                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
-                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
+                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
         curType = "addFour";
       } else if (identical) {
         var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
         if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
         else return CodeMirror.Pass;
-      } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
-                             isClosingBracket(next, pairs) ||
-                             /\s/.test(next))) {
+      } else if (opening) {
         curType = "both";
       } else {
         return CodeMirror.Pass;
@@ -175,11 +173,6 @@
     });
   }
 
-  function isClosingBracket(ch, pairs) {
-    var pos = pairs.lastIndexOf(ch);
-    return pos > -1 && pos % 2 == 1;
-  }
-
   function charsAround(cm, pos) {
     var str = cm.getRange(Pos(pos.line, pos.ch - 1),
                           Pos(pos.line, pos.ch + 1));
diff --git a/third_party/blink/renderer/devtools/front_end/cm/codemirror.css b/third_party/blink/renderer/devtools/front_end/cm/codemirror.css
index 3360cac9c..fdbf48c 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/codemirror.css
+++ b/third_party/blink/renderer/devtools/front_end/cm/codemirror.css
@@ -118,7 +118,7 @@
   .CodeMirror-linewidget {
     position: relative;
     z-index: 2;
-    overflow: auto;
+    padding: 0.1px; /* Force widget margins to stay inside of the container */
   }
 
   .CodeMirror-widget {}
diff --git a/third_party/blink/renderer/devtools/front_end/cm/codemirror.js b/third_party/blink/renderer/devtools/front_end/cm/codemirror.js
index 069c579..eaea2fa 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/codemirror.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/codemirror.js
@@ -1,7 +1,7 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
-// This is CodeMirror (http://codemirror.net), a code editor
+// This is CodeMirror (https://codemirror.net), a code editor
 // implemented in JavaScript on top of the browser's DOM.
 //
 // You can find some technical background for some of the code below
@@ -746,6 +746,16 @@
 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
 
+function collapsedSpanAround(line, ch) {
+  var sps = sawCollapsedSpans && line.markedSpans, found;
+  if (sps) { for (var i = 0; i < sps.length; ++i) {
+    var sp = sps[i];
+    if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
+        (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
+  } }
+  return found
+}
+
 // Test whether there exists a collapsed span that partially
 // overlaps (covers the start or end, but not both) of a new span.
 // Such overlap is not allowed.
@@ -2780,12 +2790,11 @@
   var lineObj = getLine(doc, lineN);
   for (;;) {
     var found = coordsCharInner(cm, lineObj, lineN, x, y);
-    var merged = collapsedSpanAtEnd(lineObj);
-    var mergedPos = merged && merged.find(0, true);
-    if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
-      { lineN = lineNo(lineObj = mergedPos.to.line); }
-    else
-      { return found }
+    var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0));
+    if (!collapsed) { return found }
+    var rangeEnd = collapsed.find(1);
+    if (rangeEnd.line == lineN) { return rangeEnd }
+    lineObj = getLine(doc, lineN = rangeEnd.line);
   }
 }
 
@@ -3545,6 +3554,7 @@
   this.cm = cm;
   var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
   var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
+  vert.tabIndex = horiz.tabIndex = -1;
   place(vert); place(horiz);
 
   on(vert, "scroll", function () {
@@ -4796,7 +4806,7 @@
 
   if ((hist.lastOp == opId ||
        hist.lastOrigin == change.origin && change.origin &&
-       ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
+       ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
         change.origin.charAt(0) == "*")) &&
       (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
     // Merge this change into the last event
@@ -5225,7 +5235,8 @@
 
 // Revert a change stored in a document's history.
 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
-  if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return }
+  var suppress = doc.cm && doc.cm.state.suppressEdits;
+  if (suppress && !allowSelectionOnly) { return }
 
   var hist = doc.history, event, selAfter = doc.sel;
   var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
@@ -5250,8 +5261,10 @@
         return
       }
       selAfter = event;
-    }
-    else { break }
+    } else if (suppress) {
+      source.push(event);
+      return
+    } else { break }
   }
 
   // Build up a reverse change object to add to the opposite history
@@ -5694,7 +5707,7 @@
   this.height = null;
   var diff = widgetHeight(this) - oldH;
   if (!diff) { return }
-  updateLineHeight(line, line.height + diff);
+  if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
   if (cm) {
     runInOp(cm, function () {
       cm.curOp.forceUpdate = true;
@@ -5727,7 +5740,7 @@
     }
     return true
   });
-  signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle));
+  if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
   return widget
 }
 
@@ -6576,8 +6589,6 @@
 // Called when the window resizes
 function onResize(cm) {
   var d = cm.display;
-  if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
-    { return }
   // Might be a text scaling operation, clear size caches.
   d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
   d.scrollbarsClipped = false;
@@ -6585,11 +6596,11 @@
 }
 
 var keyNames = {
-  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+  3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
   19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
   36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
   46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
-  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
+  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
   173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
   221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
   63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
@@ -6623,7 +6634,7 @@
   "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
   "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
   "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
-  fallthrough: "basic"
+  "fallthrough": "basic"
 };
 // Very basic readline/emacs-style bindings, which are standard on Mac.
 keyMap.emacsy = {
@@ -6641,7 +6652,7 @@
   "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
   "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
   "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
-  fallthrough: ["basic", "emacsy"]
+  "fallthrough": ["basic", "emacsy"]
 };
 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
 
@@ -6736,6 +6747,9 @@
   if (presto && event.keyCode == 34 && event["char"]) { return false }
   var name = keyNames[event.keyCode];
   if (name == null || event.altGraphKey) { return false }
+  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
+  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
+  if (event.keyCode == 3 && event.code) { name = event.code; }
   return addModifierNames(name, event, noShift)
 }
 
@@ -7318,8 +7332,8 @@
   var dragEnd = operation(cm, function (e) {
     if (webkit) { display.scroller.draggable = false; }
     cm.state.draggingText = false;
-    off(document, "mouseup", dragEnd);
-    off(document, "mousemove", mouseMove);
+    off(display.wrapper.ownerDocument, "mouseup", dragEnd);
+    off(display.wrapper.ownerDocument, "mousemove", mouseMove);
     off(display.scroller, "dragstart", dragStart);
     off(display.scroller, "drop", dragEnd);
     if (!moved) {
@@ -7328,7 +7342,7 @@
         { extendSelection(cm.doc, pos, null, null, behavior.extend); }
       // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
       if (webkit || ie && ie_version == 9)
-        { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); }
+        { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
       else
         { display.input.focus(); }
     }
@@ -7343,8 +7357,8 @@
   dragEnd.copy = !behavior.moveOnDrag;
   // IE's approach to draggable
   if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
-  on(document, "mouseup", dragEnd);
-  on(document, "mousemove", mouseMove);
+  on(display.wrapper.ownerDocument, "mouseup", dragEnd);
+  on(display.wrapper.ownerDocument, "mousemove", mouseMove);
   on(display.scroller, "dragstart", dragStart);
   on(display.scroller, "drop", dragEnd);
 
@@ -7476,19 +7490,19 @@
     counter = Infinity;
     e_preventDefault(e);
     display.input.focus();
-    off(document, "mousemove", move);
-    off(document, "mouseup", up);
+    off(display.wrapper.ownerDocument, "mousemove", move);
+    off(display.wrapper.ownerDocument, "mouseup", up);
     doc.history.lastSelOrigin = null;
   }
 
   var move = operation(cm, function (e) {
-    if (!e_button(e)) { done(e); }
+    if (e.buttons === 0 || !e_button(e)) { done(e); }
     else { extend(e); }
   });
   var up = operation(cm, done);
   cm.state.selectingText = up;
-  on(document, "mousemove", move);
-  on(document, "mouseup", up);
+  on(display.wrapper.ownerDocument, "mousemove", move);
+  on(display.wrapper.ownerDocument, "mouseup", up);
 }
 
 // Used when mouse-selecting to adjust the anchor to the proper side
@@ -7618,6 +7632,7 @@
     clearCaches(cm);
     regChange(cm);
   }, true);
+
   option("lineSeparator", null, function (cm, val) {
     cm.doc.lineSep = val;
     if (!val) { return }
@@ -7719,6 +7734,7 @@
   option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
   option("autofocus", null);
   option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
+  option("phrases", null);
 }
 
 function guttersChanged(cm) {
@@ -7770,6 +7786,7 @@
 
   var doc = options.value;
   if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
+  else if (options.mode) { doc.modeOption = options.mode; }
   this.doc = doc;
 
   var input = new CodeMirror$1.inputStyles[options.inputStyle](this);
@@ -8024,7 +8041,7 @@
 
   var paste = cm.state.pasteIncoming || origin == "paste";
   var textLines = splitLinesAuto(inserted), multiPaste = null;
-  // When pasing N lines into N selections, insert one line per selection
+  // When pasting N lines into N selections, insert one line per selection
   if (paste && sel.ranges.length > 1) {
     if (lastCopied && lastCopied.text.join("\n") == inserted) {
       if (sel.ranges.length % lastCopied.text.length == 0) {
@@ -8556,6 +8573,11 @@
       return old
     }),
 
+    phrase: function(phraseText) {
+      var phrases = this.options.phrases;
+      return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
+    },
+
     getInputField: function(){return this.display.input.getField()},
     getWrapperElement: function(){return this.display.wrapper},
     getScrollerElement: function(){return this.display.scroller},
@@ -8760,8 +8782,12 @@
   this.showMultipleSelections(info);
 };
 
+ContentEditableInput.prototype.getSelection = function () {
+  return this.cm.display.wrapper.ownerDocument.getSelection()
+};
+
 ContentEditableInput.prototype.showPrimarySelection = function () {
-  var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
+  var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
   var from = prim.from(), to = prim.to();
 
   if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
@@ -8828,13 +8854,13 @@
 };
 
 ContentEditableInput.prototype.rememberSelection = function () {
-  var sel = window.getSelection();
+  var sel = this.getSelection();
   this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
   this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
 };
 
 ContentEditableInput.prototype.selectionInEditor = function () {
-  var sel = window.getSelection();
+  var sel = this.getSelection();
   if (!sel.rangeCount) { return false }
   var node = sel.getRangeAt(0).commonAncestorContainer;
   return contains(this.div, node)
@@ -8869,14 +8895,14 @@
 };
 
 ContentEditableInput.prototype.selectionChanged = function () {
-  var sel = window.getSelection();
+  var sel = this.getSelection();
   return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
     sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
 };
 
 ContentEditableInput.prototype.pollSelection = function () {
   if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
-  var sel = window.getSelection(), cm = this.cm;
+  var sel = this.getSelection(), cm = this.cm;
   // On Android Chrome (version 56, at least), backspacing into an
   // uneditable block element will put the cursor in that element,
   // and then, because it's not editable, hide the virtual keyboard.
@@ -9010,7 +9036,7 @@
 };
 
 ContentEditableInput.prototype.onKeyPress = function (e) {
-  if (e.charCode == 0) { return }
+  if (e.charCode == 0 || this.composing) { return }
   e.preventDefault();
   if (!this.cm.isReadOnly())
     { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
@@ -9050,12 +9076,13 @@
 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
 
 function domTextBetween(cm, from, to, fromLine, toLine) {
-  var text = "", closing = false, lineSep = cm.doc.lineSeparator();
+  var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
   function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
   function close() {
     if (closing) {
       text += lineSep;
-      closing = false;
+      if (extraLinebreak) { text += lineSep; }
+      closing = extraLinebreak = false;
     }
   }
   function addText(str) {
@@ -9067,8 +9094,8 @@
   function walk(node) {
     if (node.nodeType == 1) {
       var cmText = node.getAttribute("cm-text");
-      if (cmText != null) {
-        addText(cmText || node.textContent.replace(/\u200b/g, ""));
+      if (cmText) {
+        addText(cmText);
         return
       }
       var markerID = node.getAttribute("cm-marker"), range$$1;
@@ -9079,19 +9106,24 @@
         return
       }
       if (node.getAttribute("contenteditable") == "false") { return }
-      var isBlock = /^(pre|div|p)$/i.test(node.nodeName);
+      var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
+      if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
+
       if (isBlock) { close(); }
       for (var i = 0; i < node.childNodes.length; i++)
         { walk(node.childNodes[i]); }
+
+      if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
       if (isBlock) { closing = true; }
     } else if (node.nodeType == 3) {
-      addText(node.nodeValue);
+      addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
     }
   }
   for (;;) {
     walk(from);
     if (from == to) { break }
     from = from.nextSibling;
+    extraLinebreak = false;
   }
   return text
 }
@@ -9192,13 +9224,10 @@
     var this$1 = this;
 
   var input = this, cm = this.cm;
+  this.createField(display);
+  var te = this.textarea;
 
-  // Wraps and hides input textarea
-  var div = this.wrapper = hiddenTextarea();
-  // The semihidden textarea that is focused when the editor is
-  // focused, and receives input.
-  var te = this.textarea = div.firstChild;
-  display.wrapper.insertBefore(div, display.wrapper.firstChild);
+  display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
 
   // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
   if (ios) { te.style.width = "0px"; }
@@ -9265,6 +9294,14 @@
   });
 };
 
+TextareaInput.prototype.createField = function (_display) {
+  // Wraps and hides input textarea
+  this.wrapper = hiddenTextarea();
+  // The semihidden textarea that is focused when the editor is
+  // focused, and receives input.
+  this.textarea = this.wrapper.firstChild;
+};
+
 TextareaInput.prototype.prepareSelection = function () {
   // Redraw the selection and/or cursor
   var cm = this.cm, display = cm.display, doc = cm.doc;
@@ -9658,7 +9695,7 @@
 
 addLegacyProps(CodeMirror$1);
 
-CodeMirror$1.version = "5.31.1";
+CodeMirror$1.version = "5.39.3";
 
 return CodeMirror$1;
 
diff --git a/third_party/blink/renderer/devtools/front_end/cm/comment.js b/third_party/blink/renderer/devtools/front_end/cm/comment.js
index 84c67edf..8394e85 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/comment.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/comment.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/third_party/blink/renderer/devtools/front_end/cm/markselection.js b/third_party/blink/renderer/devtools/front_end/cm/markselection.js
index 1602acc3..adfaa62d 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/markselection.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/markselection.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Because sometimes you need to mark the selected *text*.
 //
diff --git a/third_party/blink/renderer/devtools/front_end/cm/matchbrackets.js b/third_party/blink/renderer/devtools/front_end/cm/matchbrackets.js
index 4d7a230..c918c3f9 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/matchbrackets.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/matchbrackets.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -102,18 +102,23 @@
     }
   }
 
-  var currentlyHighlighted = null;
   function doMatchBrackets(cm) {
     cm.operation(function() {
-      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
-      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+      if (cm.state.matchBrackets.currentlyHighlighted) {
+        cm.state.matchBrackets.currentlyHighlighted();
+        cm.state.matchBrackets.currentlyHighlighted = null;
+      }
+      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
     });
   }
 
   CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
     if (old && old != CodeMirror.Init) {
       cm.off("cursorActivity", doMatchBrackets);
-      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+        cm.state.matchBrackets.currentlyHighlighted();
+        cm.state.matchBrackets.currentlyHighlighted = null;
+      }
     }
     if (val) {
       cm.state.matchBrackets = typeof val == "object" ? val : {};
diff --git a/third_party/blink/renderer/devtools/front_end/cm/multiplex.js b/third_party/blink/renderer/devtools/front_end/cm/multiplex.js
index 3d8b34c4..738ea98a 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/multiplex.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/multiplex.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -50,7 +50,15 @@
           if (found == stream.pos) {
             if (!other.parseDelimiters) stream.match(other.open);
             state.innerActive = other;
-            state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
+
+            // Get the outer indent, making sure to handle CodeMirror.Pass
+            var outerIndent = 0;
+            if (outer.indent) {
+              var possibleOuterIndent = outer.indent(state.outer, "");
+              if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent;
+            }
+
+            state.inner = CodeMirror.startState(other.mode, outerIndent);
             return other.delimStyle && (other.delimStyle + " " + other.delimStyle + "-open");
           } else if (found != -1 && found < cutOff) {
             cutOff = found;
diff --git a/third_party/blink/renderer/devtools/front_end/cm/overlay.js b/third_party/blink/renderer/devtools/front_end/cm/overlay.js
index 4a9f99a..839d9e55 100644
--- a/third_party/blink/renderer/devtools/front_end/cm/overlay.js
+++ b/third_party/blink/renderer/devtools/front_end/cm/overlay.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Utility function that allows modes to be combined. The mode given
 // as the base argument takes care of most of the normal mode
diff --git a/third_party/blink/renderer/devtools/front_end/cm_headless/headlesscodemirror.js b/third_party/blink/renderer/devtools/front_end/cm_headless/headlesscodemirror.js
index f7aec5b..690a123e 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_headless/headlesscodemirror.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_headless/headlesscodemirror.js
@@ -2,7 +2,7 @@
 // from CodeMirror distribution
 (function(window) {
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 window.CodeMirror = {};
 
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/clike.js b/third_party/blink/renderer/devtools/front_end/cm_modes/clike.js
index 02a8531..42033bd 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/clike.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/clike.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -216,15 +216,15 @@
     indent: function(state, textAfter) {
       if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
       var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
+      var closing = firstChar == ctx.type;
       if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
       if (parserConfig.dontIndentStatements)
         while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
           ctx = ctx.prev
       if (hooks.indent) {
-        var hook = hooks.indent(state, ctx, textAfter);
+        var hook = hooks.indent(state, ctx, textAfter, indentUnit);
         if (typeof hook == "number") return hook
       }
-      var closing = firstChar == ctx.type;
       var switchBlock = ctx.prev && ctx.prev.info == "switch";
       if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
         while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
@@ -374,7 +374,7 @@
     blockKeywords: words("case do else for if switch while struct"),
     defKeywords: words("struct"),
     typeFirstDefinitions: true,
-    atoms: words("null true false"),
+    atoms: words("NULL true false"),
     hooks: {"#": cppHook, "*": pointerHook},
     modeProps: {fold: ["brace", "include"]}
   });
@@ -390,7 +390,7 @@
     blockKeywords: words("catch class do else finally for if struct switch try while"),
     defKeywords: words("class namespace struct enum union"),
     typeFirstDefinitions: true,
-    atoms: words("true false null"),
+    atoms: words("true false NULL"),
     dontIndentStatements: /^template$/,
     isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
     hooks: {
@@ -489,6 +489,27 @@
     return "string";
   }
 
+  function tokenNestedComment(depth) {
+    return function (stream, state) {
+      var ch
+      while (ch = stream.next()) {
+        if (ch == "*" && stream.eat("/")) {
+          if (depth == 1) {
+            state.tokenize = null
+            break
+          } else {
+            state.tokenize = tokenNestedComment(depth - 1)
+            return state.tokenize(stream, state)
+          }
+        } else if (ch == "/" && stream.eat("*")) {
+          state.tokenize = tokenNestedComment(depth + 1)
+          return state.tokenize(stream, state)
+        }
+      }
+      return "comment"
+    }
+  }
+
   def("text/x-scala", {
     name: "clike",
     keywords: words(
@@ -544,6 +565,12 @@
         } else {
           return false
         }
+      },
+
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false
+        state.tokenize = tokenNestedComment(1)
+        return state.tokenize(stream, state)
       }
     },
     modeProps: {closeBrackets: {triples: '"'}}
@@ -570,34 +597,51 @@
     name: "clike",
     keywords: words(
       /*keywords*/
-      "package as typealias class interface this super val " +
-      "var fun for is in This throw return " +
+      "package as typealias class interface this super val operator " +
+      "var fun for is in This throw return annotation " +
       "break continue object if else while do try when !in !is as? " +
 
       /*soft keywords*/
       "file import where by get set abstract enum open inner override private public internal " +
       "protected catch finally out final vararg reified dynamic companion constructor init " +
       "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
-      "external annotation crossinline const operator infix suspend"
+      "external annotation crossinline const operator infix suspend actual expect setparam"
     ),
     types: words(
       /* package java.lang */
       "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
       "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
       "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
-      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
+      "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
+      "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
     ),
     intendSwitch: false,
     indentStatements: false,
     multiLineStrings: true,
-    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
     blockKeywords: words("catch class do else finally for if where try while enum"),
     defKeywords: words("class val var object interface fun"),
     atoms: words("true false null this"),
     hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
       '"': function(stream, state) {
         state.tokenize = tokenKotlinString(stream.match('""'));
         return state.tokenize(stream, state);
+      },
+      indent: function(state, ctx, textAfter, indentUnit) {
+        var firstChar = textAfter && textAfter.charAt(0);
+        if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "")
+          return state.indented;
+        if (state.prevToken == "operator" && textAfter != "}" ||
+          state.prevToken == "variable" && firstChar == "." ||
+          (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".")
+          return indentUnit * 2 + ctx.indented;
+        if (ctx.align && ctx.type == "}")
+          return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit);
       }
     },
     modeProps: {closeBrackets: {triples: '"'}}
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/clojure.js b/third_party/blink/renderer/devtools/front_end/cm_modes/clojure.js
index ed6af2c..2015edf 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/clojure.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/clojure.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Author: Hans Engel
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/coffeescript.js b/third_party/blink/renderer/devtools/front_end/cm_modes/coffeescript.js
index ae955db..a54e9d5 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/coffeescript.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/coffeescript.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Link to the project's GitHub page:
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/jsx.js b/third_party/blink/renderer/devtools/front_end/cm_modes/jsx.js
index 45c3024..2c91beb9 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/jsx.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/jsx.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -26,7 +26,7 @@
   }
 
   CodeMirror.defineMode("jsx", function(config, modeConfig) {
-    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
+    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
     var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
 
     function flatXMLIndent(state) {
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/livescript.js b/third_party/blink/renderer/devtools/front_end/cm_modes/livescript.js
index 1e363f8..595e067 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/livescript.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/livescript.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Link to the project's GitHub page:
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/markdown.js b/third_party/blink/renderer/devtools/front_end/cm_modes/markdown.js
index 61e0c4fc..442ab6b 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/markdown.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/markdown.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -90,7 +90,7 @@
   ,   setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
   ,   textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
   ,   fencedCodeRE = /^(~~~+|```+)[ \t]*([\w+#-]*)[^\n`]*$/
-  ,   linkDefRE = /^\s*\[[^\]]+?\]:\s*\S+(\s*\S*\s*)?$/ // naive link-definition
+  ,   linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition
   ,   punctuation = /[!\"#$%&\'()*+,\-\.\/:;<=>?@\[\\\]^_`{|}~\u2014]/
   ,   expandedTab = "    " // CommonMark specifies tab as 4 spaces
 
@@ -126,8 +126,17 @@
     // Reset state.indentedCode
     state.indentedCode = false;
     if (state.f == htmlBlock) {
-      state.f = inlineNormal;
-      state.block = blockNormal;
+      var exit = htmlModeMissing
+      if (!exit) {
+        var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
+        exit = inner.mode.name == "xml" && inner.state.tagStart === null &&
+          (!inner.state.context && inner.state.tokenize.isInText)
+      }
+      if (exit) {
+        state.f = inlineNormal;
+        state.block = blockNormal;
+        state.htmlState = null;
+      }
     }
     // Reset state.trailingSpace
     state.trailingSpace = 0;
@@ -497,6 +506,7 @@
     }
 
     if (ch === '[' && !state.image) {
+      if (state.linkText && stream.match(/^.*?\]/)) return getType(state)
       state.linkText = true;
       if (modeCfg.highlightFormatting) state.formatting = "link";
       return getType(state);
@@ -534,7 +544,7 @@
       return type + tokenTypes.linkEmail;
     }
 
-    if (modeCfg.xml && ch === '<' && stream.match(/^(!--|[a-z]+(?:\s+[a-z_:.\-]+(?:\s*=\s*[^ >]+)?)*\s*>)/i, false)) {
+    if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i, false)) {
       var end = stream.string.indexOf(">", stream.pos);
       if (end != -1) {
         var atts = stream.string.substring(stream.start, end);
@@ -619,7 +629,7 @@
     }
 
     if (ch === ' ') {
-      if (stream.match(/ +$/, false)) {
+      if (stream.match(/^ +$/, false)) {
         state.trailingSpace++;
       } else if (state.trailingSpace) {
         state.trailingSpaceNewLine = true;
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/php.js b/third_party/blink/renderer/devtools/front_end/cm_modes/php.js
index 589c9a6..80e2f20b 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/php.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/php.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/python.js b/third_party/blink/renderer/devtools/front_end/cm_modes/python.js
index c318793..623c03f7 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/python.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/python.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -41,7 +41,7 @@
   CodeMirror.defineMode("python", function(conf, parserConf) {
     var ERRORCLASS = "error";
 
-    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/;
+    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
     //               (Backwards-compatiblity with old, cumbersome config system)
     var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
                      parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@])/]
@@ -62,7 +62,7 @@
       var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
       myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
       myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
-      var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i");
+      var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
     } else {
       var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
       myKeywords = myKeywords.concat(["exec", "print"]);
@@ -76,9 +76,10 @@
 
     // tokenizers
     function tokenBase(stream, state) {
-      if (stream.sol()) state.indent = stream.indentation()
+      var sol = stream.sol() && state.lastToken != "\\"
+      if (sol) state.indent = stream.indentation()
       // Handle scope changes
-      if (stream.sol() && top(state).type == "py") {
+      if (sol && top(state).type == "py") {
         var scopeOffset = top(state).offset;
         if (stream.eatSpace()) {
           var lineOffset = stream.indentation();
@@ -100,13 +101,8 @@
     function tokenBaseInner(stream, state) {
       if (stream.eatSpace()) return null;
 
-      var ch = stream.peek();
-
       // Handle Comments
-      if (ch == "#") {
-        stream.skipToEnd();
-        return "comment";
-      }
+      if (stream.match(/^#.*/)) return "comment";
 
       // Handle Number Literals
       if (stream.match(/^[0-9\.]/, false)) {
@@ -146,8 +142,14 @@
 
       // Handle Strings
       if (stream.match(stringPrefixes)) {
-        state.tokenize = tokenStringFactory(stream.current());
-        return state.tokenize(stream, state);
+        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
+        if (!isFmtString) {
+          state.tokenize = tokenStringFactory(stream.current());
+          return state.tokenize(stream, state);
+        } else {
+          state.tokenize = formatStringFactory(stream.current(), state.tokenize);
+          return state.tokenize(stream, state);
+        }
       }
 
       for (var i = 0; i < operators.length; i++)
@@ -178,6 +180,77 @@
       return ERRORCLASS;
     }
 
+    function formatStringFactory(delimiter, tokenOuter) {
+      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
+        delimiter = delimiter.substr(1);
+
+      var singleline = delimiter.length == 1;
+      var OUTCLASS = "string";
+
+      function tokenFString(stream, state) {
+        // inside f-str Expression
+        if (stream.match(delimiter)) {
+          // expression ends pre-maturally, but very common in editing
+          // Could show error to remind users to close brace here
+          state.tokenize = tokenString
+          return OUTCLASS;
+        } else if (stream.match('{')) {
+          // starting brace, if not eaten below
+          return "punctuation";
+        } else if (stream.match('}')) {
+          // return to regular inside string state
+          state.tokenize = tokenString
+          return "punctuation";
+        } else {
+          // use tokenBaseInner to parse the expression
+          return tokenBaseInner(stream, state);
+        }
+      }
+
+      function tokenString(stream, state) {
+        while (!stream.eol()) {
+          stream.eatWhile(/[^'"\{\}\\]/);
+          if (stream.eat("\\")) {
+            stream.next();
+            if (singleline && stream.eol())
+              return OUTCLASS;
+          } else if (stream.match(delimiter)) {
+            state.tokenize = tokenOuter;
+            return OUTCLASS;
+          } else if (stream.match('{{')) {
+            // ignore {{ in f-str
+            return OUTCLASS;
+          } else if (stream.match('{', false)) {
+            // switch to nested mode
+            state.tokenize = tokenFString
+            if (stream.current()) {
+              return OUTCLASS;
+            } else {
+              // need to return something, so eat the starting {
+              stream.next();
+              return "punctuation";
+            }
+          } else if (stream.match('}}')) {
+            return OUTCLASS;
+          } else if (stream.match('}')) {
+            // single } in f-string is an error
+            return ERRORCLASS;
+          } else {
+            stream.eat(/['"]/);
+          }
+        }
+        if (singleline) {
+          if (parserConf.singleLineStringErrors)
+            return ERRORCLASS;
+          else
+            state.tokenize = tokenOuter;
+        }
+        return OUTCLASS;
+      }
+      tokenString.isString = true;
+      return tokenString;
+    }
+
     function tokenStringFactory(delimiter) {
       while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
         delimiter = delimiter.substr(1);
@@ -258,14 +331,16 @@
       if (current == ":" && !state.lambda && top(state).type == "py")
         pushPyScope(state);
 
-      var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
-      if (delimiter_index != -1)
-        pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+      if (current.length == 1 && !/string|comment/.test(style)) {
+        var delimiter_index = "[({".indexOf(current);
+        if (delimiter_index != -1)
+          pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
 
-      delimiter_index = "])}".indexOf(current);
-      if (delimiter_index != -1) {
-        if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
-        else return ERRORCLASS;
+        delimiter_index = "])}".indexOf(current);
+        if (delimiter_index != -1) {
+          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
+          else return ERRORCLASS;
+        }
       }
       if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
         if (state.scopes.length > 1) state.scopes.pop();
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/shell.js b/third_party/blink/renderer/devtools/front_end/cm_modes/shell.js
index 9b8b90b3..0e667e6 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/shell.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/shell.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -31,7 +31,7 @@
   // Commands
   define('builtin', 'ab awk bash beep cat cc cd chown chmod chroot clear cp ' +
     'curl cut diff echo find gawk gcc get git grep hg kill killall ln ls make ' +
-    'mkdir openssl mv nc node npm ping ps restart rm rmdir sed service sh ' +
+    'mkdir openssl mv nc nl node npm ping ps restart rm rmdir sed service sh ' +
     'shopt shred source sort sleep ssh start stop su sudo svn tee telnet top ' +
     'touch vi vim wall wc wget who write yes zsh');
 
@@ -84,29 +84,38 @@
   function tokenString(quote, style) {
     var close = quote == "(" ? ")" : quote == "{" ? "}" : quote
     return function(stream, state) {
-      var next, end = false, escaped = false;
+      var next, escaped = false;
       while ((next = stream.next()) != null) {
         if (next === close && !escaped) {
-          end = true;
+          state.tokens.shift();
           break;
-        }
-        if (next === '$' && !escaped && quote !== "'") {
+        } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) {
           escaped = true;
           stream.backUp(1);
           state.tokens.unshift(tokenDollar);
           break;
-        }
-        if (!escaped && next === quote && quote !== close) {
+        } else if (!escaped && quote !== close && next === quote) {
           state.tokens.unshift(tokenString(quote, style))
           return tokenize(stream, state)
+        } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) {
+          state.tokens.unshift(tokenStringStart(next, "string"));
+          stream.backUp(1);
+          break;
         }
         escaped = !escaped && next === '\\';
       }
-      if (end) state.tokens.shift();
       return style;
     };
   };
 
+  function tokenStringStart(quote, style) {
+    return function(stream, state) {
+      state.tokens[0] = tokenString(quote, style)
+      stream.next()
+      return tokenize(stream, state)
+    }
+  }
+
   var tokenDollar = function(stream, state) {
     if (state.tokens.length > 1) stream.eat('$');
     var ch = stream.next()
diff --git a/third_party/blink/renderer/devtools/front_end/cm_modes/stylus.js b/third_party/blink/renderer/devtools/front_end/cm_modes/stylus.js
index b83be16f..dbe241d 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_modes/stylus.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_modes/stylus.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Stylus mode created by Dmitry Kiselyov http://git.io/AaRB
 
@@ -76,7 +76,7 @@
       if (ch == "#") {
         stream.next();
         // Hex color
-        if (stream.match(/^[0-9a-f]{6}|[0-9a-f]{3}/i)) {
+        if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b/i)) {
           return ["atom", "atom"];
         }
         // ID selector
diff --git a/third_party/blink/renderer/devtools/front_end/cm_web_modes/css.js b/third_party/blink/renderer/devtools/front_end/cm_web_modes/css.js
index 00e9b3d..8b572290 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_web_modes/css.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_web_modes/css.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -77,9 +77,9 @@
       return ret("qualifier", "qualifier");
     } else if (/[:;{}\[\]\(\)]/.test(ch)) {
       return ret(null, ch);
-    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
-               (ch == "d" && stream.match("omain(")) ||
-               (ch == "r" && stream.match("egexp("))) {
+    } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
+               ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
+               ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
       stream.backUp(1);
       state.tokenize = tokenParenthesized;
       return ret("property", "word");
@@ -162,16 +162,16 @@
       return pushContext(state, stream, "block");
     } else if (type == "}" && state.context.prev) {
       return popContext(state);
-    } else if (supportsAtComponent && /@component/.test(type)) {
+    } else if (supportsAtComponent && /@component/i.test(type)) {
       return pushContext(state, stream, "atComponentBlock");
-    } else if (/^@(-moz-)?document$/.test(type)) {
+    } else if (/^@(-moz-)?document$/i.test(type)) {
       return pushContext(state, stream, "documentTypes");
-    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
+    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
       return pushContext(state, stream, "atBlock");
-    } else if (/^@(font-face|counter-style)/.test(type)) {
+    } else if (/^@(font-face|counter-style)/i.test(type)) {
       state.stateArg = type;
       return "restricted_atBlock_before";
-    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
       return "keyframes";
     } else if (type && type.charAt(0) == "@") {
       return pushContext(state, stream, "at");
@@ -793,7 +793,7 @@
       },
       "@": function(stream) {
         if (stream.eat("{")) return [null, "interpolation"];
-        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
         stream.eatWhile(/[\w\\\-]/);
         if (stream.match(/^\s*:/, false))
           return ["variable-2", "variable-definition"];
diff --git a/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlembedded.js b/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlembedded.js
index 464dc57f..439e63a 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlembedded.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlembedded.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,7 +14,16 @@
   "use strict";
 
   CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
+    var closeComment = parserConfig.closeComment || "--%>"
     return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), {
+      open: parserConfig.openComment || "<%--",
+      close: closeComment,
+      delimStyle: "comment",
+      mode: {token: function(stream) {
+        stream.skipTo(closeComment) || stream.skipToEnd()
+        return "comment"
+      }}
+    }, {
       open: parserConfig.open || parserConfig.scriptStartRegex || "<%",
       close: parserConfig.close || parserConfig.scriptEndRegex || "%>",
       mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)
diff --git a/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlmixed.js b/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlmixed.js
index 33398ec..c9925384 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlmixed.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_web_modes/htmlmixed.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js b/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js
index 139e53d..a31ffff8 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_web_modes/javascript.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -26,7 +26,7 @@
     var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
     var operator = kw("operator"), atom = {type: "atom", style: "atom"};
 
-    var jsKeywords = {
+    return {
       "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
       "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
       "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
@@ -38,33 +38,6 @@
       "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
       "await": C
     };
-
-    // Extend the 'normal' keywords with the TypeScript language extensions
-    if (isTS) {
-      var type = {type: "variable", style: "type"};
-      var tsKeywords = {
-        // object-like things
-        "interface": kw("class"),
-        "implements": C,
-        "namespace": C,
-
-        // scope modifiers
-        "public": kw("modifier"),
-        "private": kw("modifier"),
-        "protected": kw("modifier"),
-        "abstract": kw("modifier"),
-        "readonly": kw("modifier"),
-
-        // types
-        "string": type, "number": type, "boolean": type, "any": type
-      };
-
-      for (var attr in tsKeywords) {
-        jsKeywords[attr] = tsKeywords[attr];
-      }
-    }
-
-    return jsKeywords;
   }();
 
   var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
@@ -102,17 +75,10 @@
       return ret(ch);
     } else if (ch == "=" && stream.eat(">")) {
       return ret("=>", "operator");
-    } else if (ch == "0" && stream.eat(/x/i)) {
-      stream.eatWhile(/[\da-f]/i);
-      return ret("number", "number");
-    } else if (ch == "0" && stream.eat(/o/i)) {
-      stream.eatWhile(/[0-7]/i);
-      return ret("number", "number");
-    } else if (ch == "0" && stream.eat(/b/i)) {
-      stream.eatWhile(/[01]/i);
+    } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) {
       return ret("number", "number");
     } else if (/\d/.test(ch)) {
-      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+      stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/);
       return ret("number", "number");
     } else if (ch == "/") {
       if (stream.eat("*")) {
@@ -123,7 +89,7 @@
         return ret("comment", "comment");
       } else if (expressionAllowed(stream, state, 1)) {
         readRegexp(stream);
-        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
+        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
         return ret("regexp", "string-2");
       } else {
         stream.eat("=");
@@ -153,7 +119,7 @@
           var kw = keywords[word]
           return ret(kw.type, kw.style, word)
         }
-        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false))
+        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
           return ret("async", "keyword", word)
       }
       return ret("variable", "variable", word)
@@ -292,35 +258,68 @@
     pass.apply(null, arguments);
     return true;
   }
+  function inList(name, list) {
+    for (var v = list; v; v = v.next) if (v.name == name) return true
+    return false;
+  }
   function register(varname) {
-    function inList(list) {
-      for (var v = list; v; v = v.next)
-        if (v.name == varname) return true;
-      return false;
-    }
     var state = cx.state;
     cx.marked = "def";
     if (state.context) {
-      if (inList(state.localVars)) return;
-      state.localVars = {name: varname, next: state.localVars};
-    } else {
-      if (inList(state.globalVars)) return;
-      if (parserConfig.globalVars)
-        state.globalVars = {name: varname, next: state.globalVars};
+      if (state.lexical.info == "var" && state.context && state.context.block) {
+        // FIXME function decls are also not block scoped
+        var newContext = registerVarScoped(varname, state.context)
+        if (newContext != null) {
+          state.context = newContext
+          return
+        }
+      } else if (!inList(varname, state.localVars)) {
+        state.localVars = new Var(varname, state.localVars)
+        return
+      }
     }
+    // Fall through means this is global
+    if (parserConfig.globalVars && !inList(varname, state.globalVars))
+      state.globalVars = new Var(varname, state.globalVars)
+  }
+  function registerVarScoped(varname, context) {
+    if (!context) {
+      return null
+    } else if (context.block) {
+      var inner = registerVarScoped(varname, context.prev)
+      if (!inner) return null
+      if (inner == context.prev) return context
+      return new Context(inner, context.vars, true)
+    } else if (inList(varname, context.vars)) {
+      return context
+    } else {
+      return new Context(context.prev, new Var(varname, context.vars), false)
+    }
+  }
+
+  function isModifier(name) {
+    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
   }
 
   // Combinators
 
-  var defaultVars = {name: "this", next: {name: "arguments"}};
+  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
+  function Var(name, next) { this.name = name; this.next = next }
+
+  var defaultVars = new Var("this", new Var("arguments", null))
   function pushcontext() {
-    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
-    cx.state.localVars = defaultVars;
+    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
+    cx.state.localVars = defaultVars
+  }
+  function pushblockcontext() {
+    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
+    cx.state.localVars = null
   }
   function popcontext() {
-    cx.state.localVars = cx.state.context.vars;
-    cx.state.context = cx.state.context.prev;
+    cx.state.localVars = cx.state.context.vars
+    cx.state.context = cx.state.context.prev
   }
+  popcontext.lex = true
   function pushlex(type, info) {
     var result = function() {
       var state = cx.state, indent = state.indented;
@@ -345,19 +344,19 @@
   function expect(wanted) {
     function exp(type) {
       if (type == wanted) return cont();
-      else if (wanted == ";") return pass();
+      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
       else return cont(exp);
     };
     return exp;
   }
 
   function statement(type, value) {
-    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
+    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
     if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
     if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
     if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
     if (type == "debugger") return cont(expect(";"));
-    if (type == "{") return cont(pushlex("}"), block, poplex);
+    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
     if (type == ";") return cont();
     if (type == "if") {
       if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
@@ -366,44 +365,51 @@
     }
     if (type == "function") return cont(functiondef);
     if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
     if (type == "variable") {
-      if (isTS && value == "type") {
-        cx.marked = "keyword"
-        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
-      } else if (isTS && value == "declare") {
+      if (isTS && value == "declare") {
         cx.marked = "keyword"
         return cont(statement)
-      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
+      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
         cx.marked = "keyword"
-        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+        if (value == "enum") return cont(enumdef);
+        else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+      } else if (isTS && value == "namespace") {
+        cx.marked = "keyword"
+        return cont(pushlex("form"), expression, block, poplex)
+      } else if (isTS && value == "abstract") {
+        cx.marked = "keyword"
+        return cont(statement)
       } else {
         return cont(pushlex("stat"), maybelabel);
       }
     }
-    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
-                                      block, poplex, poplex);
+    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
+                                      block, poplex, poplex, popcontext);
     if (type == "case") return cont(expression, expect(":"));
     if (type == "default") return cont(expect(":"));
-    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
-                                     statement, poplex, popcontext);
-    if (type == "class") return cont(pushlex("form"), className, poplex);
+    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
     if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
     if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
     if (type == "async") return cont(statement)
     if (value == "@") return cont(expression, statement)
     return pass(pushlex("stat"), expression, expect(";"), poplex);
   }
-  function expression(type) {
-    return expressionInner(type, false);
+  function maybeCatchBinding(type) {
+    if (type == "(") return cont(funarg, expect(")"))
   }
-  function expressionNoComma(type) {
-    return expressionInner(type, true);
+  function expression(type, value) {
+    return expressionInner(type, value, false);
+  }
+  function expressionNoComma(type, value) {
+    return expressionInner(type, value, true);
   }
   function parenExpr(type) {
     if (type != "(") return pass()
     return cont(pushlex(")"), expression, expect(")"), poplex)
   }
-  function expressionInner(type, noComma) {
+  function expressionInner(type, value, noComma) {
     if (cx.state.fatArrowAt == cx.stream.start) {
       var body = noComma ? arrowBodyNoComma : arrowBody;
       if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
@@ -413,7 +419,7 @@
     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (type == "function") return cont(functiondef, maybeop);
-    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
+    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
     if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
     if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
@@ -421,6 +427,7 @@
     if (type == "{") return contCommasep(objprop, "}", null, maybeop);
     if (type == "quasi") return pass(quasi, maybeop);
     if (type == "new") return cont(maybeTarget(noComma));
+    if (type == "import") return cont(expression);
     return cont();
   }
   function maybeexpression(type) {
@@ -438,6 +445,8 @@
     if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
     if (type == "operator") {
       if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
+      if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
+        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
       if (value == "?") return cont(expression, expect(":"), expr);
       return cont(expr);
     }
@@ -509,10 +518,11 @@
       return cont(afterprop);
     } else if (type == "jsonld-keyword") {
       return cont(afterprop);
-    } else if (type == "modifier") {
+    } else if (isTS && isModifier(value)) {
+      cx.marked = "keyword"
       return cont(objprop)
     } else if (type == "[") {
-      return cont(expression, expect("]"), afterprop);
+      return cont(expression, maybetype, expect("]"), afterprop);
     } else if (type == "spread") {
       return cont(expressionNoComma, afterprop);
     } else if (value == "*") {
@@ -564,20 +574,32 @@
       if (value == "?") return cont(maybetype);
     }
   }
+  function mayberettype(type) {
+    if (isTS && type == ":") {
+      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
+      else return cont(typeexpr)
+    }
+  }
+  function isKW(_, value) {
+    if (value == "is") {
+      cx.marked = "keyword"
+      return cont()
+    }
+  }
   function typeexpr(type, value) {
+    if (value == "keyof" || value == "typeof") {
+      cx.marked = "keyword"
+      return cont(value == "keyof" ? typeexpr : expressionNoComma)
+    }
     if (type == "variable" || value == "void") {
-      if (value == "keyof") {
-        cx.marked = "keyword"
-        return cont(typeexpr)
-      } else {
-        cx.marked = "type"
-        return cont(afterType)
-      }
+      cx.marked = "type"
+      return cont(afterType)
     }
     if (type == "string" || type == "number" || type == "atom") return cont(afterType);
     if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
     if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
     if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
+    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
   }
   function maybeReturnType(type) {
     if (type == "=>") return cont(typeexpr)
@@ -594,15 +616,16 @@
       return cont(expression, maybetype, expect("]"), typeprop)
     }
   }
-  function typearg(type) {
-    if (type == "variable") return cont(typearg)
-    else if (type == ":") return cont(typeexpr)
+  function typearg(type, value) {
+    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
+    if (type == ":") return cont(typeexpr)
+    return pass(typeexpr)
   }
   function afterType(type, value) {
     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
-    if (value == "|" || type == ".") return cont(typeexpr)
+    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
     if (type == "[") return cont(expect("]"), afterType)
-    if (value == "extends") return cont(typeexpr)
+    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
   }
   function maybeTypeArgs(_, value) {
     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
@@ -613,11 +636,12 @@
   function maybeTypeDefault(_, value) {
     if (value == "=") return cont(typeexpr)
   }
-  function vardef() {
+  function vardef(_, value) {
+    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
     return pass(pattern, maybetype, maybeAssign, vardefCont);
   }
   function pattern(type, value) {
-    if (type == "modifier") return cont(pattern)
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
     if (type == "variable") { register(value); return cont(); }
     if (type == "spread") return cont(pattern);
     if (type == "[") return contCommasep(pattern, "]");
@@ -642,7 +666,8 @@
   function maybeelse(type, value) {
     if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
   }
-  function forspec(type) {
+  function forspec(type, value) {
+    if (value == "await") return cont(forspec);
     if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
   }
   function forspec1(type) {
@@ -666,12 +691,13 @@
   function functiondef(type, value) {
     if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
     if (type == "variable") {register(value); return cont(functiondef);}
-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
     if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
   }
   function funarg(type, value) {
     if (value == "@") cont(expression, funarg)
-    if (type == "spread" || type == "modifier") return cont(funarg);
+    if (type == "spread") return cont(funarg);
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
     return pass(pattern, maybetype, maybeAssign);
   }
   function classExpression(type, value) {
@@ -684,14 +710,16 @@
   }
   function classNameAfter(type, value) {
     if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
-    if (value == "extends" || value == "implements" || (isTS && type == ","))
+    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
+      if (value == "implements") cx.marked = "keyword";
       return cont(isTS ? typeexpr : expression, classNameAfter);
+    }
     if (type == "{") return cont(pushlex("}"), classBody, poplex);
   }
   function classBody(type, value) {
-    if (type == "modifier" || type == "async" ||
+    if (type == "async" ||
         (type == "variable" &&
-         (value == "static" || value == "get" || value == "set") &&
+         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
          cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
       cx.marked = "keyword";
       return cont(classBody);
@@ -701,7 +729,7 @@
       return cont(isTS ? classfield : functiondef, classBody);
     }
     if (type == "[")
-      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
+      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
     if (value == "*") {
       cx.marked = "keyword";
       return cont(classBody);
@@ -728,6 +756,7 @@
   }
   function afterImport(type) {
     if (type == "string") return cont();
+    if (type == "(") return pass(expression);
     return pass(importSpec, maybeMoreImports, maybeFrom);
   }
   function importSpec(type, value) {
@@ -749,6 +778,12 @@
     if (type == "]") return cont();
     return pass(commasep(expressionNoComma, "]"));
   }
+  function enumdef() {
+    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
+  }
+  function enummember() {
+    return pass(pattern, maybeAssign);
+  }
 
   function isContinuedStatement(state, textAfter) {
     return state.lastType == "operator" || state.lastType == "," ||
@@ -772,7 +807,7 @@
         cc: [],
         lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
         localVars: parserConfig.localVars,
-        context: parserConfig.localVars && {vars: parserConfig.localVars},
+        context: parserConfig.localVars && new Context(null, null, false),
         indented: basecolumn || 0
       };
       if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
@@ -813,7 +848,7 @@
         lexical = lexical.prev;
       var type = lexical.type, closing = firstChar == type;
 
-      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "stat")
diff --git a/third_party/blink/renderer/devtools/front_end/cm_web_modes/xml.js b/third_party/blink/renderer/devtools/front_end/cm_web_modes/xml.js
index a365739..b67bf850 100644
--- a/third_party/blink/renderer/devtools/front_end/cm_web_modes/xml.js
+++ b/third_party/blink/renderer/devtools/front_end/cm_web_modes/xml.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -52,6 +52,7 @@
   doNotIndent: {},
   allowUnquoted: false,
   allowMissing: false,
+  allowMissingTagName: false,
   caseFold: false
 }
 
@@ -162,8 +163,9 @@
         stream.next();
       }
       return style;
-    };
+    }
   }
+
   function doctype(depth) {
     return function(stream, state) {
       var ch;
@@ -226,6 +228,9 @@
       state.tagName = stream.current();
       setStyle = "tag";
       return attrState;
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return attrState(type, stream, state);
     } else {
       setStyle = "error";
       return tagNameState;
@@ -244,6 +249,9 @@
         setStyle = "tag error";
         return closeStateErr;
       }
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return closeState(type, stream, state);
     } else {
       setStyle = "error";
       return closeStateErr;
@@ -391,4 +399,4 @@
 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
   CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
 
-});
\ No newline at end of file
+});
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 7de4e37..ee4d73ed 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -235,6 +235,16 @@
   return ToLayoutBoxModelObject(layout_object_);
 }
 
+bool IsProgrammaticallyScrollable(LayoutBox* box) {
+  if (!box->HasOverflowClip()) {
+    // If overflow is visible it is not scrollable.
+    return false;
+  }
+  // Return true if the content is larger than the available space.
+  return box->PixelSnappedScrollWidth() != box->PixelSnappedClientWidth() ||
+         box->PixelSnappedScrollHeight() != box->PixelSnappedClientHeight();
+}
+
 ScrollableArea* AXLayoutObject::GetScrollableAreaIfScrollable() const {
   if (IsWebArea())
     return DocumentFrameView()->LayoutViewport();
@@ -243,10 +253,24 @@
     return nullptr;
 
   LayoutBox* box = ToLayoutBox(layout_object_);
-  if (!box->CanBeScrolledAndHasScrollableArea())
-    return nullptr;
 
-  return box->GetScrollableArea();
+  // This should possibly use box->CanBeScrolledAndHasScrollableArea() as it
+  // used to; however, accessibility must consider any kind of non-visible
+  // overflow as programmatically scrollable. Unfortunately
+  // LayoutBox::CanBeScrolledAndHasScrollableArea() method calls
+  // LayoutBox::CanBeProgramaticallyScrolled() which does not consider
+  // visibility:hidden content to be programmatically scrollable, although it
+  // certainly is, and can even be scrolled by selecting and using shift+arrow
+  // keys. It should be noticed that the new code used here reduces the overall
+  // amount of work as well.
+  // It is not sufficient to expose it only in the anoymous child, because that
+  // child is truncated in platform accessibility trees, which present the
+  // textfield as a leaf.
+  ScrollableArea* scrollable_area = box->GetScrollableArea();
+  if (scrollable_area && IsProgrammaticallyScrollable(box))
+    return scrollable_area;
+
+  return nullptr;
 }
 
 static bool IsImageOrAltText(LayoutBoxModelObject* box, Node* node) {
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.cc b/third_party/blink/renderer/modules/webaudio/audio_node.cc
index 1fcc1af..07b429f9 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.cc
@@ -593,7 +593,18 @@
 // ----------------------------------------------------------------
 
 AudioNode::AudioNode(BaseAudioContext& context)
-    : context_(context), handler_(nullptr) {}
+    : context_(context),
+      deferred_task_handler_(&context.GetDeferredTaskHandler()),
+      handler_(nullptr) {}
+
+AudioNode::~AudioNode() {
+  // The graph lock is required to destroy the handler. And we can't use
+  // |context_| to touch it, since that object may also be a dead heap object.
+  {
+    DeferredTaskHandler::GraphAutoLocker locker(*deferred_task_handler_);
+    handler_ = nullptr;
+  }
+}
 
 void AudioNode::Dispose() {
   DCHECK(IsMainThread());
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.h b/third_party/blink/renderer/modules/webaudio/audio_node.h
index e16e959..be391ad 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node.h
@@ -47,6 +47,7 @@
 class AudioNodeInput;
 class AudioNodeOutput;
 class AudioParam;
+class DeferredTaskHandler;
 class ExceptionState;
 
 // An AudioNode is the basic building block for handling audio within an
@@ -314,6 +315,8 @@
   USING_PRE_FINALIZER(AudioNode, Dispose);
 
  public:
+  ~AudioNode() override;
+
   void Trace(blink::Visitor*) override;
   AudioHandler& Handler() const;
 
@@ -367,7 +370,12 @@
   bool DisconnectFromOutputIfConnected(unsigned output_index, AudioParam&);
 
   Member<BaseAudioContext> context_;
+
+  // Needed in the destructor, where |context_| is not guaranteed to be alive.
+  scoped_refptr<DeferredTaskHandler> deferred_task_handler_;
+
   scoped_refptr<AudioHandler> handler_;
+
   // Represents audio node graph with Oilpan references. N-th HeapHashSet
   // represents a set of AudioNode objects connected to this AudioNode's N-th
   // output.
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.cc b/third_party/blink/renderer/modules/webaudio/audio_param.cc
index 2d596c59c..b1bbb8c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.cc
@@ -339,7 +339,8 @@
                                          rate_mode,
                                          min_value,
                                          max_value)),
-      context_(context) {}
+      context_(context),
+      deferred_task_handler_(&context.GetDeferredTaskHandler()) {}
 
 AudioParam* AudioParam::Create(BaseAudioContext& context,
                                AudioParamType param_type,
@@ -364,6 +365,15 @@
                         min_value, max_value);
 }
 
+AudioParam::~AudioParam() {
+  // The graph lock is required to destroy the handler. And we can't use
+  // |context_| to touch it, since that object may also be a dead heap object.
+  {
+    DeferredTaskHandler::GraphAutoLocker locker(*deferred_task_handler_);
+    handler_ = nullptr;
+  }
+}
+
 void AudioParam::Trace(blink::Visitor* visitor) {
   visitor->Trace(context_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param.h b/third_party/blink/renderer/modules/webaudio/audio_param.h
index 96c782d..1dfb251c 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_param.h
@@ -270,6 +270,8 @@
       float min_value = -std::numeric_limits<float>::max(),
       float max_value = std::numeric_limits<float>::max());
 
+  ~AudioParam() override;
+
   void Trace(blink::Visitor*) override;
   // |handler| always returns a valid object.
   AudioParamHandler& Handler() const { return *handler_; }
@@ -325,6 +327,8 @@
   scoped_refptr<AudioParamHandler> handler_;
   Member<BaseAudioContext> context_;
 
+  // Needed in the destructor, where |context_| is not guaranteed to be alive.
+  scoped_refptr<DeferredTaskHandler> deferred_task_handler_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc b/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc
index 4146dd2..54ad0e30 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_summing_junction.cc
@@ -33,6 +33,7 @@
     : deferred_task_handler_(&handler), rendering_state_need_updating_(false) {}
 
 AudioSummingJunction::~AudioSummingJunction() {
+  DCHECK(GetDeferredTaskHandler().IsGraphOwner());
   GetDeferredTaskHandler().RemoveMarkedSummingJunction(this);
 }
 
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 2fd3b9fd..e4300a3 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -109,6 +109,13 @@
       output_position_() {}
 
 BaseAudioContext::~BaseAudioContext() {
+  {
+    // We may need to destroy summing junctions, which must happen while this
+    // object is still valid and with the graph lock held.
+    GraphAutoLocker locker(this);
+    destination_handler_ = nullptr;
+  }
+
   GetDeferredTaskHandler().ContextWillBeDestroyed();
   DCHECK(!active_source_nodes_.size());
   DCHECK(!is_resolving_resume_promises_);
diff --git a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
index d0115b1..11a4b26f 100644
--- a/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
+++ b/third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc
@@ -101,7 +101,7 @@
 void DeferredTaskHandler::RemoveMarkedSummingJunction(
     AudioSummingJunction* summing_junction) {
   DCHECK(IsMainThread());
-  GraphAutoLocker locker(*this);
+  DCHECK(IsGraphOwner());
   dirty_summing_junctions_.erase(summing_junction);
 }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/database.cc b/third_party/blink/renderer/modules/webdatabase/database.cc
index 1bad37d..d3c597ee4 100644
--- a/third_party/blink/renderer/modules/webdatabase/database.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database.cc
@@ -51,7 +51,6 @@
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_transaction.h"
 #include "third_party/blink/renderer/modules/webdatabase/storage_log.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/waitable_event.h"
 #include "third_party/blink/renderer/platform/web_task_runner.h"
 #include "third_party/blink/renderer/platform/wtf/atomics.h"
@@ -100,7 +99,7 @@
   // lifetime of the process.
   DatabaseGuid RegisterOriginAndName(const String& origin, const String& name)
       EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
-    CheckLocked();
+    mutex_.AssertAcquired();
     String string_id = origin + "/" + name;
     DCHECK(string_id.IsSafeToSendToAnotherThread());
     DatabaseGuid guid = origin_name_to_guid_.at(string_id);
@@ -116,7 +115,7 @@
   // RegisterOriginAndName). If all uses are released, the cached version will
   // be erased from memory.
   void ReleaseGuid(DatabaseGuid guid) EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
-    CheckLocked();
+    mutex_.AssertAcquired();
     DCHECK(count_.Contains(guid));
     if (count_.erase(guid))
       guid_to_version_.erase(guid);
@@ -124,7 +123,7 @@
 
   // The null string is returned only if the cached version has not been set.
   String GetVersion(DatabaseGuid guid) const EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
-    CheckLocked();
+    mutex_.AssertAcquired();
     return guid_to_version_.at(guid).IsolatedCopy();
   }
 
@@ -132,19 +131,13 @@
   // The null string is treated as the empty string.
   void SetVersion(DatabaseGuid guid, const String& new_version)
       EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
-    CheckLocked();
+    mutex_.AssertAcquired();
     guid_to_version_.Set(guid, new_version.IsNull()
                                    ? g_empty_string
                                    : new_version.IsolatedCopy());
   }
 
  private:
-  void CheckLocked() const ASSERT_EXCLUSIVE_LOCK(mutex_) {
-#if DCHECK_IS_ON()
-    DCHECK(mutex_.Locked());
-#endif
-  }
-
   mutable Mutex mutex_;
   HashMap<String, DatabaseGuid> origin_name_to_guid_ GUARDED_BY(mutex_);
   HashCountedSet<DatabaseGuid> count_ GUARDED_BY(mutex_);
@@ -375,9 +368,7 @@
 }
 
 void Database::ScheduleTransaction() {
-#if DCHECK_IS_ON()
-  DCHECK(transaction_in_progress_mutex_.Locked());  // Locked by caller.
-#endif                                              // DCHECK_IS_ON()
+  transaction_in_progress_mutex_.AssertAcquired();
   SQLTransactionBackend* transaction = nullptr;
 
   if (is_transaction_queue_enabled_ && !transaction_queue_.IsEmpty())
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc
index c5a2891..987281c 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_file_system.cc
@@ -32,7 +32,6 @@
 
 #include "sql/initialization.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
 #include "third_party/sqlite/sqlite3.h"
 
diff --git a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_statement.cc b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_statement.cc
index bd362db..5b443d1 100644
--- a/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_statement.cc
+++ b/third_party/blink/renderer/modules/webdatabase/sqlite/sqlite_statement.cc
@@ -28,7 +28,6 @@
 #include <memory>
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sql_log.h"
 #include "third_party/blink/renderer/modules/webdatabase/sqlite/sql_value.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
 #include "third_party/sqlite/sqlite3.h"
diff --git a/third_party/blink/renderer/platform/graphics/image_frame_generator.cc b/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
index 660c755..3b0a7a07 100644
--- a/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
+++ b/third_party/blink/renderer/platform/graphics/image_frame_generator.cc
@@ -239,9 +239,7 @@
     SkBitmap::Allocator& allocator,
     ImageDecoder::AlphaOption alpha_option,
     ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option) {
-#if DCHECK_IS_ON()
-  DCHECK(decode_mutex_.Locked());
-#endif
+  decode_mutex_.AssertAcquired();
 
   TRACE_EVENT1("blink", "ImageFrameGenerator::tryToResumeDecode", "frame index",
                static_cast<int>(index));
@@ -334,9 +332,7 @@
     ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option,
     const SkISize& scaled_size,
     bool& used_external_allocator) {
-#if DCHECK_IS_ON()
-  DCHECK(decode_mutex_.Locked());
-#endif
+  decode_mutex_.AssertAcquired();
   TRACE_EVENT2("blink", "ImageFrameGenerator::decode", "width",
                full_size_.width(), "height", full_size_.height());
 
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index 3859f8c..337d4cb 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -69,7 +69,6 @@
     "persistent_node.h",
     "process_heap.cc",
     "process_heap.h",
-    "safe_point.h",
     "self_keep_alive.h",
     "sparse_heap_bitmap.cc",
     "sparse_heap_bitmap.h",
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index ba99288..f3d2435 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -44,7 +44,6 @@
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
 #include "third_party/blink/renderer/platform/heap/page_memory.h"
 #include "third_party/blink/renderer/platform/heap/page_pool.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
@@ -320,13 +319,13 @@
   thread_state_->VisitPersistents(visitor);
 }
 
-void ThreadHeap::VisitStackRoots(MarkingVisitor* visitor) {
+void ThreadHeap::VisitStackRoots() {
   ThreadHeapStatsCollector::Scope stats_scope(
       stats_collector(), ThreadHeapStatsCollector::kVisitStackRoots);
   DCHECK(thread_state_->InAtomicMarkingPause());
   address_cache_->FlushIfDirty();
   address_cache_->EnableLookup();
-  thread_state_->VisitStack(visitor);
+  thread_state_->PushRegistersAndVisitStack();
   address_cache_->DisableLookup();
 }
 
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 708710a..ac45c5b 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -204,9 +204,7 @@
   }
 
   void VisitPersistentRoots(Visitor*);
-  void VisitStackRoots(MarkingVisitor*);
-  void EnterSafePoint(ThreadState*);
-  void LeaveSafePoint();
+  void VisitStackRoots();
 
   // Is the finalizable GC object still alive, but slated for lazy sweeping?
   // If a lazy sweep is in progress, returns true if the object was found
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index 67b2e27..c5552d6 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -42,7 +42,6 @@
 #include "third_party/blink/renderer/platform/heap/marking_verifier.h"
 #include "third_party/blink/renderer/platform/heap/page_memory.h"
 #include "third_party/blink/renderer/platform/heap/page_pool.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index d84ed20..4e7a71d 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -47,7 +47,6 @@
 #include "third_party/blink/renderer/platform/heap/heap_terminated_array_builder.h"
 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/heap/self_keep_alive.h"
 #include "third_party/blink/renderer/platform/heap/stack_frame_depth.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
@@ -361,9 +360,9 @@
   ~TestGCCollectGarbageScope() { ThreadState::Current()->CompleteSweep(); }
 };
 
-class TestGCMarkingScope : public TestGCCollectGarbageScope {
+class TestGCScope : public TestGCCollectGarbageScope {
  public:
-  explicit TestGCMarkingScope(BlinkGC::StackState state)
+  explicit TestGCScope(BlinkGC::StackState state)
       : TestGCCollectGarbageScope(state),
         atomic_pause_scope_(ThreadState::Current()) {
     ThreadState::Current()->Heap().stats_collector()->NotifyMarkingStarted(
@@ -371,7 +370,7 @@
     ThreadState::Current()->AtomicPausePrologue(state, BlinkGC::kAtomicMarking,
                                                 BlinkGC::GCReason::kPreciseGC);
   }
-  ~TestGCMarkingScope() {
+  ~TestGCScope() {
     ThreadState::Current()->MarkPhaseEpilogue(BlinkGC::kAtomicMarking);
     ThreadState::Current()->AtomicPauseEpilogue(BlinkGC::kAtomicMarking,
                                                 BlinkGC::kEagerSweeping);
@@ -381,16 +380,6 @@
   ThreadState::AtomicPauseScope atomic_pause_scope_;
 };
 
-class TestGCScope : public TestGCMarkingScope {
- public:
-  explicit TestGCScope(BlinkGC::StackState state)
-      : TestGCMarkingScope(state), safe_point_scope_(state) {}
-  ~TestGCScope() {}
-
- private:
-  SafePointScope safe_point_scope_;
-};
-
 class SimpleObject : public GarbageCollected<SimpleObject> {
  public:
   static SimpleObject* Create() { return new SimpleObject(); }
diff --git a/third_party/blink/renderer/platform/heap/persistent.h b/third_party/blink/renderer/platform/heap/persistent.h
index b5bfab9..d15be07f 100644
--- a/third_party/blink/renderer/platform/heap/persistent.h
+++ b/third_party/blink/renderer/platform/heap/persistent.h
@@ -186,7 +186,7 @@
         crossThreadnessConfiguration == kCrossThreadPersistentConfiguration,
         "This Persistent does not require the cross-thread lock.");
 #if DCHECK_IS_ON()
-    DCHECK(ProcessHeap::CrossThreadPersistentMutex().Locked());
+    ProcessHeap::CrossThreadPersistentMutex().AssertAcquired();
 #endif
     raw_ = nullptr;
     CrossThreadPersistentRegion& region =
@@ -342,7 +342,7 @@
                      kWeakPersistentConfiguration,
                      kCrossThreadPersistentConfiguration>* persistent) {
 #if DCHECK_IS_ON()
-    DCHECK(ProcessHeap::CrossThreadPersistentMutex().Locked());
+    ProcessHeap::CrossThreadPersistentMutex().AssertAcquired();
 #endif
     persistent->ClearWithLockHeld();
   }
diff --git a/third_party/blink/renderer/platform/heap/persistent_node.h b/third_party/blink/renderer/platform/heap/persistent_node.h
index 1255bf4..d3234653 100644
--- a/third_party/blink/renderer/platform/heap/persistent_node.h
+++ b/third_party/blink/renderer/platform/heap/persistent_node.h
@@ -174,7 +174,7 @@
                               void* self,
                               TraceCallback trace) {
 #if DCHECK_IS_ON()
-    DCHECK(ProcessHeap::CrossThreadPersistentMutex().Locked());
+    ProcessHeap::CrossThreadPersistentMutex().AssertAcquired();
 #endif
     PersistentNode* node =
         persistent_region_.AllocatePersistentNode(self, trace);
@@ -204,7 +204,7 @@
   void TracePersistentNodes(Visitor* visitor) {
 // If this assert triggers, you're tracing without being in a LockScope.
 #if DCHECK_IS_ON()
-    DCHECK(ProcessHeap::CrossThreadPersistentMutex().Locked());
+    ProcessHeap::CrossThreadPersistentMutex().AssertAcquired();
 #endif
     persistent_region_.TracePersistentNodes(
         visitor, CrossThreadPersistentRegion::ShouldTracePersistentNode);
diff --git a/third_party/blink/renderer/platform/heap/process_heap.cc b/third_party/blink/renderer/platform/heap/process_heap.cc
index a8fcc0a..61ccc62 100644
--- a/third_party/blink/renderer/platform/heap/process_heap.cc
+++ b/third_party/blink/renderer/platform/heap/process_heap.cc
@@ -14,8 +14,10 @@
 
 namespace {
 
-void BlinkGCAllocHook(uint8_t* address, size_t size, const char*) {
-  base::SamplingHeapProfiler::RecordAlloc(address, size);
+void BlinkGCAllocHook(uint8_t* address, size_t size, const char* context) {
+  base::SamplingHeapProfiler::RecordAlloc(
+      address, size, base::SamplingHeapProfiler::AllocatorType::kBlinkGC,
+      context);
 }
 
 void BlinkGCFreeHook(uint8_t* address) {
diff --git a/third_party/blink/renderer/platform/heap/safe_point.h b/third_party/blink/renderer/platform/heap/safe_point.h
deleted file mode 100644
index 41a3e7b..0000000
--- a/third_party/blink/renderer/platform/heap/safe_point.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SAFE_POINT_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_SAFE_POINT_H_
-
-#include "base/macros.h"
-#include "third_party/blink/renderer/platform/heap/thread_state.h"
-#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
-
-namespace blink {
-
-class SafePointScope final {
-  STACK_ALLOCATED();
-
- public:
-  explicit SafePointScope(BlinkGC::StackState stack_state,
-                          ThreadState* state = ThreadState::Current())
-      : state_(state) {
-    if (state_) {
-      state_->EnterSafePoint(stack_state, this);
-    }
-  }
-
-  ~SafePointScope() {
-    if (state_)
-      state_->LeaveSafePoint();
-  }
-
- private:
-  ThreadState* state_;
-
-  DISALLOW_COPY_AND_ASSIGN(SafePointScope);
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 87ab2ad..76a2e74ca 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -51,7 +51,6 @@
 #include "third_party/blink/renderer/platform/heap/heap_stats_collector.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
 #include "third_party/blink/renderer/platform/heap/page_pool.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
@@ -168,13 +167,11 @@
       weak_persistent_region_(std::make_unique<PersistentRegion>()),
       start_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
       end_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
-      safe_point_scope_marker_(nullptr),
 #if HAS_FEATURE(safe_stack)
       start_of_unsafe_stack_(
           reinterpret_cast<intptr_t*>(__builtin___get_unsafe_stack_top())),
       end_of_unsafe_stack_(
           reinterpret_cast<intptr_t*>(__builtin___get_unsafe_stack_bottom())),
-      safe_point_scope_unsafe_marker_(nullptr),
 #endif
       sweep_forbidden_(false),
       no_allocation_count_(0),
@@ -331,24 +328,15 @@
 NO_SANITIZE_ADDRESS
 NO_SANITIZE_THREAD
 void ThreadState::VisitStack(MarkingVisitor* visitor) {
-  if (stack_state_ == BlinkGC::kNoHeapPointersOnStack)
-    return;
+  DCHECK_EQ(current_gc_data_.stack_state, BlinkGC::kHeapPointersOnStack);
 
   Address* start = reinterpret_cast<Address*>(start_of_stack_);
-  // If there is a safepoint scope marker we should stop the stack
-  // scanning there to not touch active parts of the stack. Anything
-  // interesting beyond that point is in the safepoint stack copy.
-  // If there is no scope marker the thread is blocked and we should
-  // scan all the way to the recorded end stack pointer.
   Address* end = reinterpret_cast<Address*>(end_of_stack_);
-  Address* safe_point_scope_marker =
-      reinterpret_cast<Address*>(safe_point_scope_marker_);
-  Address* current = safe_point_scope_marker ? safe_point_scope_marker : end;
 
   // Ensure that current is aligned by address size otherwise the loop below
   // will read past start address.
-  current = reinterpret_cast<Address*>(reinterpret_cast<intptr_t>(current) &
-                                       ~(sizeof(Address) - 1));
+  Address* current = reinterpret_cast<Address*>(
+      reinterpret_cast<intptr_t>(end) & ~(sizeof(Address) - 1));
 
   for (; current < start; ++current) {
     Address ptr = *current;
@@ -367,9 +355,7 @@
 #if HAS_FEATURE(safe_stack)
   start = reinterpret_cast<Address*>(start_of_unsafe_stack_);
   end = reinterpret_cast<Address*>(end_of_unsafe_stack_);
-  safe_point_scope_marker =
-      reinterpret_cast<Address*>(safe_point_scope_unsafe_marker_);
-  current = safe_point_scope_marker ? safe_point_scope_marker : end;
+  current = end;
 
   for (; current < start; ++current) {
     Address ptr = *current;
@@ -378,15 +364,6 @@
     VisitAsanFakeStackForPointer(visitor, ptr);
   }
 #endif
-
-  for (Address ptr : safe_point_stack_copy_) {
-#if defined(MEMORY_SANITIZER)
-    // See the comment above.
-    __msan_unpoison(&ptr, sizeof(ptr));
-#endif
-    heap_->CheckAndMarkPointer(visitor, ptr);
-    VisitAsanFakeStackForPointer(visitor, ptr);
-  }
 }
 
 void ThreadState::VisitPersistents(Visitor* visitor) {
@@ -1254,72 +1231,26 @@
   DCHECK(CheckThread());
 
   RunScheduledGC(stack_state);
-  stack_state_ = BlinkGC::kHeapPointersOnStack;
 }
 
-#ifdef ADDRESS_SANITIZER
-// When we are running under AddressSanitizer with
-// detect_stack_use_after_return=1 then stack marker obtained from
-// SafePointScope will point into a fake stack.  Detect this case by checking if
-// it falls in between current stack frame and stack start and use an arbitrary
-// high enough value for it.  Don't adjust stack marker in any other case to
-// match behavior of code running without AddressSanitizer.
-NO_SANITIZE_ADDRESS static void* AdjustScopeMarkerForAdressSanitizer(
-    void* scope_marker) {
-  Address start = reinterpret_cast<Address>(WTF::GetStackStart());
-  Address end = reinterpret_cast<Address>(&start);
-  CHECK_LT(end, start);
-
-  if (end <= scope_marker && scope_marker < start)
-    return scope_marker;
-
-  // 256 is as good an approximation as any else.
-  const size_t kBytesToCopy = sizeof(Address) * 256;
-  if (static_cast<size_t>(start - end) < kBytesToCopy)
-    return start;
-
-  return end + kBytesToCopy;
-}
-#endif
-
 // TODO(haraken): The first void* pointer is unused. Remove it.
 using PushAllRegistersCallback = void (*)(void*, ThreadState*, intptr_t*);
 extern "C" void PushAllRegisters(void*, ThreadState*, PushAllRegistersCallback);
 
-static void EnterSafePointAfterPushRegisters(void*,
-                                             ThreadState* state,
-                                             intptr_t* stack_end) {
+static void DidPushRegisters(void*, ThreadState* state, intptr_t* stack_end) {
   state->RecordStackEnd(stack_end);
 #if HAS_FEATURE(safe_stack)
   state->RecordUnsafeStackEnd(
       reinterpret_cast<intptr_t*>(__builtin___get_unsafe_stack_ptr()));
 #endif
-  state->CopyStackUntilSafePointScope();
 }
 
-void ThreadState::EnterSafePoint(BlinkGC::StackState stack_state,
-                                 void* scope_marker) {
+void ThreadState::PushRegistersAndVisitStack() {
   DCHECK(CheckThread());
-#ifdef ADDRESS_SANITIZER
-  if (stack_state == BlinkGC::kHeapPointersOnStack)
-    scope_marker = AdjustScopeMarkerForAdressSanitizer(scope_marker);
-#endif
-  DCHECK(stack_state == BlinkGC::kNoHeapPointersOnStack || scope_marker);
   DCHECK(IsGCForbidden());
-  stack_state_ = stack_state;
-#if HAS_FEATURE(safe_stack)
-  safe_point_scope_marker_ = __builtin_frame_address(0);
-  safe_point_scope_unsafe_marker_ = scope_marker;
-#else
-  safe_point_scope_marker_ = scope_marker;
-#endif
-  PushAllRegisters(nullptr, this, EnterSafePointAfterPushRegisters);
-}
-
-void ThreadState::LeaveSafePoint() {
-  DCHECK(CheckThread());
-  stack_state_ = BlinkGC::kHeapPointersOnStack;
-  ClearSafePointScopeMarker();
+  DCHECK_EQ(current_gc_data_.stack_state, BlinkGC::kHeapPointersOnStack);
+  PushAllRegisters(nullptr, this, DidPushRegisters);
+  VisitStack(static_cast<MarkingVisitor*>(CurrentVisitor()));
 }
 
 void ThreadState::AddObserver(BlinkGCObserver* observer) {
@@ -1346,47 +1277,6 @@
   reported_memory_to_v8_ = current_heap_size;
 }
 
-void ThreadState::CopyStackUntilSafePointScope() {
-  if (!safe_point_scope_marker_ ||
-      stack_state_ == BlinkGC::kNoHeapPointersOnStack)
-    return;
-
-  Address* to = reinterpret_cast<Address*>(safe_point_scope_marker_);
-  Address* from = reinterpret_cast<Address*>(end_of_stack_);
-  CHECK_LT(from, to);
-  CHECK_LE(to, reinterpret_cast<Address*>(start_of_stack_));
-  size_t slot_count = static_cast<size_t>(to - from);
-
-#if HAS_FEATURE(safe_stack)
-  Address* unsafe_to =
-      reinterpret_cast<Address*>(safe_point_scope_unsafe_marker_);
-  Address* unsafe_from = reinterpret_cast<Address*>(end_of_unsafe_stack_);
-  CHECK_LE(unsafe_from, unsafe_to);
-  CHECK_LE(unsafe_to, reinterpret_cast<Address*>(start_of_unsafe_stack_));
-  size_t unsafe_slot_count = static_cast<size_t>(unsafe_to - unsafe_from);
-#else
-  constexpr size_t unsafe_slot_count = 0;
-#endif
-
-// Catch potential performance issues.
-#if defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
-  // ASan/LSan use more space on the stack and we therefore
-  // increase the allowed stack copying for those builds.
-  DCHECK_LT(slot_count + unsafe_slot_count, 2048u);
-#else
-  DCHECK_LT(slot_count + unsafe_slot_count, 1024u);
-#endif
-
-  DCHECK(!safe_point_stack_copy_.size());
-  safe_point_stack_copy_.resize(slot_count + unsafe_slot_count);
-  for (size_t i = 0; i < slot_count; ++i)
-    safe_point_stack_copy_[i] = from[i];
-#if HAS_FEATURE(safe_stack)
-  for (size_t i = 0; i < unsafe_slot_count; ++i)
-    safe_point_stack_copy_[slot_count + i] = unsafe_from[i];
-#endif
-}
-
 void ThreadState::RegisterStaticPersistentNode(
     PersistentNode* node,
     PersistentClearCallback callback) {
@@ -1724,10 +1614,8 @@
   Heap().VisitPersistentRoots(current_gc_data_.visitor.get());
 
   // 2. Trace objects reachable from the stack.
-  {
-    SafePointScope safe_point_scope(current_gc_data_.stack_state, this);
-    Heap().VisitStackRoots(current_gc_data_.visitor.get());
-  }
+  if (current_gc_data_.stack_state == BlinkGC::kHeapPointersOnStack)
+    Heap().VisitStackRoots();
 }
 
 bool ThreadState::MarkPhaseAdvanceMarking(TimeTicks deadline) {
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 224858a0a..3a5f663 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -408,42 +408,16 @@
 
   void FlushHeapDoesNotContainCacheIfNeeded();
 
-  // Safepoint related functionality.
-  //
-  // When a thread attempts to perform GC it needs to stop all other threads
-  // that use the heap or at least guarantee that they will not touch any
-  // heap allocated object until GC is complete.
-  //
-  // We say that a thread is at a safepoint if this thread is guaranteed to
-  // not touch any heap allocated object or any heap related functionality until
-  // it leaves the safepoint.
-  //
-  // Notice that a thread does not have to be paused if it is at safepoint it
-  // can continue to run and perform tasks that do not require interaction
-  // with the heap. It will be paused if it attempts to leave the safepoint and
-  // there is a GC in progress.
-  //
-  // Each thread that has ThreadState attached must:
-  //   - periodically check if GC is requested from another thread by calling a
-  //     safePoint() method;
-  //   - use SafePointScope around long running loops that have no safePoint()
-  //     invocation inside, such loops must not touch any heap object;
-  //
-  // Check if GC is requested by another thread and pause this thread if this is
-  // the case.  Can only be called when current thread is in a consistent state.
   void SafePoint(BlinkGC::StackState);
 
-  // Mark current thread as running inside safepoint.
-  void EnterSafePoint(BlinkGC::StackState, void*);
-  void LeaveSafePoint();
-
   void RecordStackEnd(intptr_t* end_of_stack) { end_of_stack_ = end_of_stack; }
 #if HAS_FEATURE(safe_stack)
   void RecordUnsafeStackEnd(intptr_t* end_of_unsafe_stack) {
     end_of_unsafe_stack_ = end_of_unsafe_stack;
   }
 #endif
-  NO_SANITIZE_ADDRESS void CopyStackUntilSafePointScope();
+
+  void PushRegistersAndVisitStack();
 
   // A region of non-weak PersistentNodes allocated on the given thread.
   PersistentRegion* GetPersistentRegion() const {
@@ -534,8 +508,6 @@
 
   v8::Isolate* GetIsolate() const { return isolate_; }
 
-  BlinkGC::StackState GetStackState() const { return stack_state_; }
-
   void CollectGarbage(BlinkGC::StackState,
                       BlinkGC::MarkingType,
                       BlinkGC::SweepingType,
@@ -590,7 +562,7 @@
   friend class incremental_marking_test::IncrementalMarkingTestDriver;
   template <typename T>
   friend class PrefinalizerRegistration;
-  friend class TestGCMarkingScope;
+  friend class TestGCScope;
   friend class ThreadStateSchedulingTest;
 
   // Number of ThreadState's that are currently in incremental marking. The
@@ -623,14 +595,6 @@
                       BlinkGC::SweepingType,
                       BlinkGC::GCReason);
 
-  void ClearSafePointScopeMarker() {
-    safe_point_stack_copy_.clear();
-    safe_point_scope_marker_ = nullptr;
-#if HAS_FEATURE(safe_stack)
-    safe_point_scope_unsafe_marker_ = nullptr;
-#endif
-  }
-
   bool ShouldVerifyMarking() const;
 
   // shouldScheduleIdleGC and shouldForceConservativeGC
@@ -683,7 +647,6 @@
 
   void ReportMemoryToV8();
 
-  friend class SafePointScope;
 
   friend class BlinkGCObserver;
 
@@ -712,18 +675,14 @@
   ThreadIdentifier thread_;
   std::unique_ptr<PersistentRegion> persistent_region_;
   std::unique_ptr<PersistentRegion> weak_persistent_region_;
-  BlinkGC::StackState stack_state_;
   intptr_t* start_of_stack_;
   intptr_t* end_of_stack_;
-  void* safe_point_scope_marker_;
 
 #if HAS_FEATURE(safe_stack)
   intptr_t* start_of_unsafe_stack_;
   intptr_t* end_of_unsafe_stack_;
-  void* safe_point_scope_unsafe_marker_;
 #endif
 
-  Vector<Address> safe_point_stack_copy_;
   bool sweep_forbidden_;
   size_t no_allocation_count_;
   size_t gc_forbidden_count_;
diff --git a/third_party/blink/renderer/platform/waitable_event.cc b/third_party/blink/renderer/platform/waitable_event.cc
index 677113ba..453b9ac1 100644
--- a/third_party/blink/renderer/platform/waitable_event.cc
+++ b/third_party/blink/renderer/platform/waitable_event.cc
@@ -7,7 +7,6 @@
 #include <vector>
 #include "base/optional.h"
 #include "base/synchronization/waitable_event.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/web_thread_supporting_gc.cc b/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
index fd0cd5f9..046c4d3 100644
--- a/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
+++ b/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/renderer/platform/heap/safe_point.h"
 #include "third_party/blink/renderer/platform/memory_coordinator.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
diff --git a/third_party/blink/renderer/platform/wtf/threading_primitives.h b/third_party/blink/renderer/platform/wtf/threading_primitives.h
index f112896..a96d6de 100644
--- a/third_party/blink/renderer/platform/wtf/threading_primitives.h
+++ b/third_party/blink/renderer/platform/wtf/threading_primitives.h
@@ -72,7 +72,12 @@
   void lock();
   void unlock();
 #if DCHECK_IS_ON()
-  bool Locked() { return mutex_.recursion_count_ > 0; }
+  // Deprecated in favour of AssertAcquired.
+  bool Locked() const { return mutex_.recursion_count_ > 0; }
+
+  void AssertAcquired() const { DCHECK(Locked()); }
+#else
+  void AssertAcquired() const {}
 #endif
 
  public:
@@ -91,10 +96,13 @@
   Mutex() : MutexBase(false) {}
   bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true);
 
-  // lock() and unlock() are overridden solely for the purpose of annotating
-  // them. The compiler is expected to optimize the calls away.
+  // Overridden solely for the purpose of annotating them.
+  // The compiler is expected to optimize the calls away.
   void lock() EXCLUSIVE_LOCK_FUNCTION() { MutexBase::lock(); }
   void unlock() UNLOCK_FUNCTION() { MutexBase::unlock(); }
+  void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK() {
+    MutexBase::AssertAcquired();
+  }
 };
 
 class WTF_EXPORT RecursiveMutex : public MutexBase {
diff --git a/third_party/hunspell/google/bdict_reader.cc b/third_party/hunspell/google/bdict_reader.cc
index 358a8e262..de51580 100644
--- a/third_party/hunspell/google/bdict_reader.cc
+++ b/third_party/hunspell/google/bdict_reader.cc
@@ -76,7 +76,7 @@
   // additional affix IDs following the node when leaf_has_following is set,
   // but this will not handle those.
   inline int affix_id_for_leaf() const {
-    if (node_offset_ >= bdict_length_ - 2) {
+    if (node_offset_ >= bdict_length_ - 1) {
       is_valid_ = false;
       return 0;
     }
diff --git a/third_party/material_design_icons/BUILD.gn b/third_party/material_design_icons/BUILD.gn
index 473c523..68f6444 100644
--- a/third_party/material_design_icons/BUILD.gn
+++ b/third_party/material_design_icons/BUILD.gn
@@ -37,6 +37,7 @@
   "file/ic_file_download",
   "hardware/ic_desktop_windows",
   "hardware/ic_desktop_windows_white",
+  "hardware/ic_keyboard",
   "hardware/ic_keyboard_arrow_down",
   "hardware/ic_keyboard_arrow_right",
   "hardware/ic_keyboard_arrow_up",
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index d9a889f..3a4d4b2 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=1
+CLANG_SUB_REVISION=2
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
@@ -307,18 +307,6 @@
     f.write('endif (CHROMIUM_TOOLS_SRC)\n')
 
 
-def DownloadHostGcc(args):
-  """Downloads gcc 4.8.5 and makes sure args.gcc_toolchain is set."""
-  if not sys.platform.startswith('linux') or args.gcc_toolchain:
-    return
-  gcc_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'gcc485precise')
-  if not os.path.exists(gcc_dir):
-    print 'Downloading pre-built GCC 4.8.5...'
-    DownloadAndUnpack(
-        CDS_URL + '/tools/gcc485precise.tgz', LLVM_BUILD_TOOLS_DIR)
-  args.gcc_toolchain = gcc_dir
-
-
 def AddSvnToPathOnWin():
   """Download svn.exe and add it to PATH."""
   if sys.platform != 'win32':
@@ -501,7 +489,6 @@
       print 'Removing old lib dir: %s' % old_lib_dir
       RmTree(old_lib_dir)
 
-  DownloadHostGcc(args)
   AddCMakeToPath(args)
   AddGnuWinToPath()
 
@@ -514,21 +501,6 @@
 
   cc, cxx = None, None
   libstdcpp = None
-  if args.gcc_toolchain:  # This option is only used on Linux.
-    # Use the specified gcc installation for building.
-    cc = os.path.join(args.gcc_toolchain, 'bin', 'gcc')
-    cxx = os.path.join(args.gcc_toolchain, 'bin', 'g++')
-
-    if not os.access(cc, os.X_OK):
-      print 'Invalid --gcc-toolchain: "%s"' % args.gcc_toolchain
-      print '"%s" does not appear to be valid.' % cc
-      return 1
-
-    # Set LD_LIBRARY_PATH to make auxiliary targets (tablegen, bootstrap
-    # compiler, etc.) find the .so.
-    libstdcpp = subprocess.check_output(
-        [cxx, '-print-file-name=libstdc++.so.6']).rstrip()
-    os.environ['LD_LIBRARY_PATH'] = os.path.dirname(libstdcpp)
 
   cflags = []
   cxxflags = []
@@ -578,11 +550,6 @@
       cc = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang')
       cxx = os.path.join(LLVM_BOOTSTRAP_INSTALL_DIR, 'bin', 'clang++')
 
-    if args.gcc_toolchain:
-      # Tell the bootstrap compiler to use a specific gcc prefix to search
-      # for standard library headers and shared object files.
-      cflags = ['--gcc-toolchain=' + args.gcc_toolchain]
-      cxxflags = ['--gcc-toolchain=' + args.gcc_toolchain]
     print 'Building final compiler'
 
   # LLVM uses C++11 starting in llvm 3.5. On Linux, this means libstdc++4.7+ is
diff --git a/tools/determinism/OWNERS b/tools/determinism/OWNERS
index 6401254..c5f88d4a 100644
--- a/tools/determinism/OWNERS
+++ b/tools/determinism/OWNERS
@@ -2,6 +2,9 @@
 # https://crbug.com/314403
 
 dpranke@chromium.org
+erikchen@chromium.org
 maruel@chromium.org
 sebmarchand@chromium.org
+thakis@chromium.org
+tikuta@chromium.org
 yyanagisawa@chromium.org
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index 1d65f032..f33e6ef 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -84,7 +84,8 @@
     "structures": [12000],
   },
   "chrome/browser/resources/chromeos/multidevice_setup/multidevice_setup_resources.grd": {
-    "structures": [12040],
+    "includes": [12040],
+    "structures": [12045],
   },
   "chrome/browser/resources/component_extension_resources.grd": {
     "includes": [12100],
diff --git a/tools/json_schema_compiler/json_schema_api.gni b/tools/json_schema_compiler/json_schema_api.gni
index 61cc368..6529482f 100644
--- a/tools/json_schema_compiler/json_schema_api.gni
+++ b/tools/json_schema_compiler/json_schema_api.gni
@@ -53,49 +53,21 @@
   "$compiler_root/util_cc_helper.py",
 ]
 
-# Outputs either the bundle of extension function registrations or the bundle of
-# generated JSON strings for each API.
+# Outputs the bundle of generated JSON strings for each API.
 #
 # Template-specific variables (in addition to the common ones described above):
 #
-# bundle [optional, default = false]
-#   Boolean indicating if the schema bundle files should be generated.
-#
-# bundle_registration [optional, default = false]
-#   Boolean indicating if the API registration bundle files should be generated.
-#
 # bundle_name [required]
 #   A string to prepend to generated bundle class names, so that multiple
 #   bundle rules can be used without conflicting.  Only used with one of
 #   the cpp-bundle generators.
-#
-# impl_dir [required if bundle_registration = true, otherwise unused]
-#   The path containing C++ implementations of API functions. This path is
-#   used as the root path when looking for {schema}/{schema}_api.h headers
-#   when generating API registration bundles. Such headers, if found, are
-#   automatically included by the generated code.
-#
-# uncompiled_sources [optional]
-#   A list of schema files which should not be compiled, but which should still
-#   be processed for API bundle generation.
-#
-# uncompiled_bundle_schema_sources [optional, only used when bundle = true]
-#   A list of schema files which should not be compiled nor registered, but
-#   should still be processed for the schema generation.
-#
-# TODO(devlin): Break this up into two targets, one for extension function
-# registration and one for generated JSON strings. https://crbug.com/864576.
-template("json_schema_api") {
+template("generated_json_strings") {
   assert(defined(invoker.sources),
          "\"sources\" must be defined for the $target_name template.")
   assert(defined(invoker.root_namespace),
          "\"root_namespace\" must be defined for the $target_name template.")
-
-  bundle = defined(invoker.bundle) && invoker.bundle
-  bundle_registration =
-      defined(invoker.bundle_registration) && invoker.bundle_registration
-  assert(bundle || bundle_registration,
-         "$target_name must specify \"bundle\" or \"bundle_registration\".")
+  assert(defined(invoker.bundle_name),
+         "\"bundle_name\" must be defined for bundles")
 
   schema_include_rules = ""
   if (defined(invoker.schema_include_rules)) {
@@ -114,101 +86,34 @@
   # target_name variable.
   root_target_name = target_name
 
-  if (bundle) {
-    assert(defined(invoker.bundle_name),
-           "\"bundle_name\" must be defined for bundles")
-
-    uncompiled_sources = []
-    if (defined(invoker.uncompiled_sources)) {
-      uncompiled_sources = invoker.uncompiled_sources
-    }
-
-    uncompiled_bundle_schema_sources = []
-    if (defined(invoker.uncompiled_bundle_schema_sources)) {
-      uncompiled_bundle_schema_sources =
-          invoker.uncompiled_bundle_schema_sources
-    }
-
-    bundle_generator_schema_name = target_name + "_bundle_generator_schema"
-    action(bundle_generator_schema_name) {
-      visibility = [ ":$root_target_name" ]
-      script = compiler_script
-      inputs = compiler_sources + invoker.sources + uncompiled_sources +
-               uncompiled_bundle_schema_sources
-      outputs = [
-        "$target_gen_dir/generated_schemas.cc",
-        "$target_gen_dir/generated_schemas.h",
-      ]
-      args = [
-               "--root=" + rebase_path("//", root_build_dir),
-               "--destdir=" + rebase_path(root_gen_dir, root_build_dir),
-               "--namespace=$root_namespace",
-               "--bundle-name=" + invoker.bundle_name,
-               "--generator=cpp-bundle-schema",
-               "--include-rules=$schema_include_rules",
-             ] + rebase_path(invoker.sources, root_build_dir) +
-             rebase_path(uncompiled_sources, root_build_dir) +
-             rebase_path(uncompiled_bundle_schema_sources, root_build_dir)
-    }
-  }
-
-  if (bundle_registration) {
-    assert(defined(invoker.bundle_name),
-           "\"bundle_name\" must be defined for bundle registrations")
-
-    uncompiled_sources = []
-    if (defined(invoker.uncompiled_sources)) {
-      uncompiled_sources = invoker.uncompiled_sources
-    }
-
-    assert(defined(invoker.impl_dir),
-           "\"impl_dir\" must be defined for the $target_name template.")
-
-    # Child directory inside the generated file tree.
-    gen_child_dir = rebase_path(invoker.impl_dir, "//")
-
-    bundle_generator_registration_name =
-        target_name + "_bundle_generator_registration"
-    action(bundle_generator_registration_name) {
-      visibility = [ ":$root_target_name" ]
-      script = compiler_script
-      inputs = compiler_sources + invoker.sources + uncompiled_sources
-      outputs = [
-        "$root_gen_dir/$gen_child_dir/generated_api_registration.cc",
-        "$root_gen_dir/$gen_child_dir/generated_api_registration.h",
-      ]
-      args = [
-               "--root=" + rebase_path("//", root_build_dir),
-               "--destdir=" + rebase_path(root_gen_dir, root_build_dir),
-               "--namespace=$root_namespace",
-               "--bundle-name=" + invoker.bundle_name,
-               "--generator=cpp-bundle-registration",
-               "--impl-dir=$gen_child_dir",
-               "--include-rules=$schema_include_rules",
-             ] + rebase_path(invoker.sources, root_build_dir) +
-             rebase_path(uncompiled_sources, root_build_dir)
-    }
+  bundle_generator_schema_name = target_name + "_bundle_generator_schema"
+  action(bundle_generator_schema_name) {
+    visibility = [ ":$root_target_name" ]
+    script = compiler_script
+    inputs = compiler_sources + invoker.sources
+    outputs = [
+      "$target_gen_dir/generated_schemas.cc",
+      "$target_gen_dir/generated_schemas.h",
+    ]
+    args = [
+             "--root=" + rebase_path("//", root_build_dir),
+             "--destdir=" + rebase_path(root_gen_dir, root_build_dir),
+             "--namespace=$root_namespace",
+             "--bundle-name=" + invoker.bundle_name,
+             "--generator=cpp-bundle-schema",
+             "--include-rules=$schema_include_rules",
+           ] + rebase_path(invoker.sources, root_build_dir)
   }
 
   # Compute the contents of the library/source set.
-  lib_sources = invoker.sources
-  lib_deps = []
-  lib_public_deps = []
+  lib_sources =
+      invoker.sources + get_target_outputs(":$bundle_generator_schema_name")
+  lib_deps = [ ":$bundle_generator_schema_name" ]
   lib_extra_configs = []
   if (defined(invoker.configs)) {
     lib_extra_configs += invoker.configs
   }
 
-  if (bundle) {
-    lib_sources += get_target_outputs(":$bundle_generator_schema_name")
-    lib_deps += [ ":$bundle_generator_schema_name" ]
-  }
-
-  if (bundle_registration) {
-    lib_sources += get_target_outputs(":$bundle_generator_registration_name")
-    lib_deps += [ ":$bundle_generator_registration_name" ]
-  }
-
   if (defined(invoker.deps)) {
     lib_deps += invoker.deps
   }
@@ -216,7 +121,98 @@
   static_library(target_name) {
     sources = lib_sources
     deps = lib_deps
-    public_deps = lib_public_deps
+    public_deps = []
+    configs += lib_extra_configs
+    public_configs = [ ":$generated_config_name" ]
+
+    if (defined(invoker.visibility)) {
+      visibility = invoker.visibility
+    }
+  }
+}
+
+# Outputs the bundle of extension function registrations.
+#
+# Template-specific variables (in addition to the common ones described above):
+#
+# bundle_name [required]
+#   A string to prepend to generated bundle class names, so that multiple
+#   bundle rules can be used without conflicting.  Only used with one of
+#   the cpp-bundle generators.
+#
+# impl_dir [required if bundle_registration = true, otherwise unused]
+#   The path containing C++ implementations of API functions. This path is
+#   used as the root path when looking for {schema}/{schema}_api.h headers
+#   when generating API registration bundles. Such headers, if found, are
+#   automatically included by the generated code.
+template("function_registration") {
+  assert(defined(invoker.sources),
+         "\"sources\" must be defined for the $target_name template.")
+  assert(defined(invoker.root_namespace),
+         "\"root_namespace\" must be defined for the $target_name template.")
+  assert(defined(invoker.bundle_name),
+         "\"bundle_name\" must be defined for bundle registrations")
+  assert(defined(invoker.impl_dir),
+         "\"impl_dir\" must be defined for the $target_name template.")
+
+  schema_include_rules = ""
+  if (defined(invoker.schema_include_rules)) {
+    schema_include_rules = invoker.schema_include_rules
+  }
+
+  generated_config_name = target_name + "_generated_config"
+  config(generated_config_name) {
+    include_dirs = [ root_gen_dir ]
+  }
+
+  root_namespace = invoker.root_namespace
+
+  # Save the target_name, since other targets (like the action() and
+  # action_foreach() below) need to reference them, but would have their own
+  # target_name variable.
+  root_target_name = target_name
+
+  # Child directory inside the generated file tree.
+  gen_child_dir = rebase_path(invoker.impl_dir, "//")
+
+  bundle_generator_registration_name =
+      target_name + "_bundle_generator_registration"
+  action(bundle_generator_registration_name) {
+    visibility = [ ":$root_target_name" ]
+    script = compiler_script
+    inputs = compiler_sources + invoker.sources
+    outputs = [
+      "$root_gen_dir/$gen_child_dir/generated_api_registration.cc",
+      "$root_gen_dir/$gen_child_dir/generated_api_registration.h",
+    ]
+    args = [
+             "--root=" + rebase_path("//", root_build_dir),
+             "--destdir=" + rebase_path(root_gen_dir, root_build_dir),
+             "--namespace=$root_namespace",
+             "--bundle-name=" + invoker.bundle_name,
+             "--generator=cpp-bundle-registration",
+             "--impl-dir=$gen_child_dir",
+             "--include-rules=$schema_include_rules",
+           ] + rebase_path(invoker.sources, root_build_dir)
+  }
+
+  # Compute the contents of the library/source set.
+  lib_sources = invoker.sources +
+                get_target_outputs(":$bundle_generator_registration_name")
+  lib_deps = [ ":$bundle_generator_registration_name" ]
+  lib_extra_configs = []
+  if (defined(invoker.configs)) {
+    lib_extra_configs += invoker.configs
+  }
+
+  if (defined(invoker.deps)) {
+    lib_deps += invoker.deps
+  }
+
+  static_library(target_name) {
+    sources = lib_sources
+    deps = lib_deps
+    public_deps = []
     configs += lib_extra_configs
     public_configs = [ ":$generated_config_name" ]
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index f0f132d2..578d314d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -38534,6 +38534,7 @@
   <int value="2" label="Service Worker error"/>
   <int value="3" label="event.waitUntil promise rejected"/>
   <int value="4" label="Database error"/>
+  <int value="5" label="No permission granted"/>
 </enum>
 
 <enum name="PlatformStateStoreLoadResult">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 47283ba..0844278 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -39881,8 +39881,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.AutomaticGainControlMac" enum="BooleanEnabled"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.AutomaticGainControlMac" enum="BooleanEnabled">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Indicates if the Automatic Gain Control (AGC) is enabled or not. Only
@@ -40163,7 +40165,10 @@
 </histogram>
 
 <histogram name="Media.Audio.InputBufferSizeWasChangedAudioWorkedMac"
-    enum="BooleanChanged" expires_after="2018-08-30">
+    enum="BooleanChanged">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Indicates if the size of the audio unit's IO buffer was changed when
@@ -40235,7 +40240,10 @@
 </histogram>
 
 <histogram name="Media.Audio.InputStartWasDeferredAudioWorkedMac"
-    enum="BooleanDeferred" expires_after="2018-08-30">
+    enum="BooleanDeferred">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Indicates if audio capturing started with a small delay or not. Sampled each
@@ -40246,8 +40254,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.InputStartWasDeferredMac" enum="BooleanDeferred"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.InputStartWasDeferredMac" enum="BooleanDeferred">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Indicates if audio capturing started with a small delay or not. Only sampled
@@ -40255,8 +40265,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.IsOnBatteryPowerMac" enum="BooleanOnBattery"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.IsOnBatteryPowerMac" enum="BooleanOnBattery">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Indicates if the Mac OSX device is on battery power or not. Only sampled
@@ -40272,8 +40284,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.NumberOfBasicInputStreamsMac"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.NumberOfBasicInputStreamsMac">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Number of created default input audio streams. Only sampled when
@@ -40281,8 +40295,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.NumberOfLowLatencyInputStreamsMac"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.NumberOfLowLatencyInputStreamsMac">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Number of created low-latency input audio streams. Only sampled when
@@ -40290,8 +40306,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.NumberOfOutputStreamsMac"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.NumberOfOutputStreamsMac">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Number of created output audio streams. Only sampled when
@@ -40644,7 +40662,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.ResumeEventsMac" expires_after="2018-08-30">
+<histogram name="Media.Audio.ResumeEventsMac">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Counts the number of times the system has resumed from power suspension.
@@ -40653,8 +40674,10 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.UptimeMac" units="hours"
-    expires_after="2018-08-30">
+<histogram name="Media.Audio.UptimeMac" units="hours">
+  <obsolete>
+    Deprecated as of Aug 2018.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Reports the system uptime in hours for Mac OS X devices. Only sampled when
@@ -42315,7 +42338,7 @@
 </histogram>
 
 <histogram name="Media.MicrophoneMuted" enum="MicrophoneMuteResult"
-    expires_after="2018-08-30">
+    expires_after="2019-08-30">
   <owner>henrika@chromium.org</owner>
   <summary>
     Heuristically detects if the user has muted the microphone or not. Measured
@@ -42323,7 +42346,11 @@
   </summary>
 </histogram>
 
-<histogram name="Media.MicrophoneVolume" units="%" expires_after="2018-08-30">
+<histogram name="Media.MicrophoneVolume" units="%">
+  <obsolete>
+    Deprecated 08/2018. Histogram Eraser marked this histogram as unnecessary.
+    See https://crbug.com/871467 for details.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <summary>
     Level of the microphone volume measured in percent. This value can be larger
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index e05efaf..08399b0d 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -19,6 +19,8 @@
 <ukm-configuration>
 
 <event name="AbusiveExperienceHeuristic">
+  <owner>csharrison@chromium.org</owner>
+  <owner>yaoxia@chromium.org</owner>
   <summary>
     Various metrics recording experiences which are commonly used for abusive
     purposes.
@@ -28,6 +30,16 @@
       True if the page attempted a tab-under navigation.
     </summary>
   </metric>
+  <metric name="DidWindowOpenFromAdScript">
+    <summary>
+      True if the page called window.open with an ad script in the stack.
+    </summary>
+  </metric>
+  <metric name="DidWindowOpenFromAdSubframe">
+    <summary>
+      True if the page called window.open() from an ad subframe.
+    </summary>
+  </metric>
 </event>
 
 <event name="Autofill.CardUploadDecision">
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 04e4a99..2dc99c86 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -54,6 +54,9 @@
 crbug.com/752611 [ Linux ] loading.desktop/uol.com.br_cold [ Skip ]
 crbug.com/752611 [ Linux ] loading.desktop/uol.com.br_warm [ Skip ]
 crbug.com/867836 [ All ] loading.desktop/Elmundo_warm [ Skip ]
+crbug.com/876636 [ ChromeOS ] loading.desktop/TheOnion_cold [ Skip ]
+crbug.com/876636 [ ChromeOS ] loading.desktop/TheOnion_warm [ Skip ]
+crbug.com/876636 [ ChromeOS ] loading.desktop/AllRecipes_warm [ Skip ]
 
 # Benchmark: loading.mobile
 crbug.com/656861 [ All ] loading.mobile/G1 [ Skip ]
diff --git a/tools/perf/flakiness_cli b/tools/perf/flakiness_cli
index 9e4291f..ba0dc441 100755
--- a/tools/perf/flakiness_cli
+++ b/tools/perf/flakiness_cli
@@ -9,9 +9,13 @@
 import sys
 
 from test_results import api
+from test_results import frames
 
 
 def main():
+  if frames.pandas is None:
+    return 'ERROR: This tool requires pandas to run, try: pip install pandas'
+
   parser = argparse.ArgumentParser()
   subparsers = parser.add_subparsers(dest='action')
   subparsers.required = True
@@ -23,7 +27,8 @@
   args = parser.parse_args()
 
   if args.action == 'builders':
-    print api.GetBuilders()
+    data = api.GetBuilders()
+    print frames.BuildersDataFrame(data)
   elif args.action == 'results':
     print api.GetTestResults(args.master, args.builder, args.test_type)
   else:
diff --git a/tools/perf/measurements/rendering_unittest.py b/tools/perf/measurements/rendering_unittest.py
index e9528da..d3381a3f 100644
--- a/tools/perf/measurements/rendering_unittest.py
+++ b/tools/perf/measurements/rendering_unittest.py
@@ -44,12 +44,15 @@
         else:
           num_samples[histogram_name] = current_num_samples
 
-    # Check the existence of cores_per_second metrics.
-    for thread_group in RENDERING_THREAD_GROUPS:
-      # We should have at least two sample values for each metric, since
-      # pageset_repeat is 2.
-      self.assertGreater(
-          num_samples.get('cores_per_second_%s_thread' % thread_group, 0), 1)
+    # Check the existence of thread_*_cpu_time_per_second_tbmv2 metrics.
+    # TODO(crbug.com/876276): The following block should be uncommented after
+    # crrev.com/c/1181702 is rolled.
+    #
+    # for thread_group in RENDERING_THREAD_GROUPS:
+    #   # We should have at least two sample values for each metric, since
+    #   # pageset_repeat is 2.
+    #   histograme_name = 'thread_%s_cpu_time_per_second_tbmv2' % thread_group
+    #   self.assertGreater(num_samples.get(histogram_name, 0), 1)
 
     # Check the existence of some of the legacy metrics.
     self.assertGreater(num_samples.get('frame_times', 0), 1)
diff --git a/tools/perf/test_results/frames.py b/tools/perf/test_results/frames.py
new file mode 100644
index 0000000..02a8a43
--- /dev/null
+++ b/tools/perf/test_results/frames.py
@@ -0,0 +1,21 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Module to convert json responses from test-results into data frames."""
+
+try:
+  import pandas
+except ImportError:
+  pandas = None
+
+
+def BuildersDataFrame(data):
+  def iter_rows():
+    for master in data['masters']:
+      for test_type, builders in master['tests'].iteritems():
+        for builder_name in builders['builders']:
+          yield master['name'], builder_name, test_type
+
+  return pandas.DataFrame.from_records(
+      iter_rows(), columns=('master', 'builder', 'test_type'))
diff --git a/tools/perf/test_results/frames_unittest.py b/tools/perf/test_results/frames_unittest.py
new file mode 100644
index 0000000..c477186
--- /dev/null
+++ b/tools/perf/test_results/frames_unittest.py
@@ -0,0 +1,66 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from test_results import frames
+
+
+@unittest.skipIf(frames.pandas is None, 'pandas module not available')
+class TestDataFrames(unittest.TestCase):
+  def testBuildersDataFrame(self):
+    sample_data = {
+        'masters': [
+            {
+                'name': 'chromium.perf',
+                'tests': {
+                    'all_platforms_test': {
+                        'builders': [
+                            'my-mac-bot',
+                            'my-linux-bot',
+                            'my-android-bot'
+                        ]
+                    },
+                    'desktop_test': {
+                        'builders': [
+                            'my-mac-bot',
+                            'my-linux-bot'
+                        ]
+                    },
+                    'mobile_test': {
+                        'builders': [
+                            'my-android-bot'
+                        ]
+                    }
+                }
+            },
+            {
+                'name': 'chromium.perf.fyi',
+                'tests': {
+                    'mobile_test': {
+                        'builders': [
+                            'my-new-android-bot'
+                        ]
+                    }
+                }
+            }
+        ]
+    }
+    df = frames.BuildersDataFrame(sample_data)
+    # Poke and check a few simple facts about our sample data.
+    # There are two masters: chromium.perf, chromium.perf.fyi.
+    self.assertItemsEqual(
+        df['master'].unique(), ['chromium.perf', 'chromium.perf.fyi'])
+    # The 'desktop_test' runs on desktop builders only.
+    self.assertItemsEqual(
+        df[df['test_type'] == 'desktop_test']['builder'].unique(),
+        ['my-mac-bot', 'my-linux-bot'])
+    # The 'mobile_test' runs on mobile builders only.
+    self.assertItemsEqual(
+        df[df['test_type'] == 'mobile_test']['builder'].unique(),
+        ['my-android-bot', 'my-new-android-bot'])
+    # The new android bot is on the chromium.perf.fyi waterfall.
+    self.assertItemsEqual(
+        df[df['builder'] == 'my-new-android-bot']['master'].unique(),
+        ['chromium.perf.fyi'])
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 62d4699..35569970 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -176,6 +176,7 @@
  <item id="payments_sync_cards" hash_code="95588446" type="0" content_hash_code="56526513" os_list="linux,windows" file_path="components/autofill/core/browser/payments/payments_client.cc"/>
  <item id="pdf_plugin_placeholder" hash_code="56866367" type="0" content_hash_code="16907221" os_list="linux,windows" file_path="chrome/browser/plugins/pdf_plugin_placeholder_observer.cc"/>
  <item id="pepper_tcp_socket" hash_code="120623198" type="0" content_hash_code="55474823" os_list="linux,windows" file_path="content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc"/>
+ <item id="pepper_udp_socket_message_filter" hash_code="19997224" type="0" content_hash_code="62688533" os_list="linux,mac,windows" file_path="content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc"/>
  <item id="per_user_topic_registration_request" hash_code="10498172" type="0" content_hash_code="32495619" os_list="linux,windows" file_path="components/invalidation/impl/per_user_topic_registration_request.cc"/>
  <item id="permission_reporting" hash_code="131741641" type="0" deprecated="2018-03-06" content_hash_code="7213535" file_path=""/>
  <item id="permission_request_creator" hash_code="43206794" type="0" content_hash_code="73571699" os_list="linux,windows" file_path="chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc"/>
@@ -214,7 +215,7 @@
  <item id="safe_browsing_client_side_phishing_detector" hash_code="1313982" type="0" content_hash_code="50199143" os_list="linux,windows" file_path="chrome/browser/safe_browsing/client_side_detection_service.cc"/>
  <item id="safe_browsing_extended_reporting" hash_code="42848942" type="0" content_hash_code="50089173" os_list="linux,windows" file_path="components/safe_browsing/ping_manager.cc"/>
  <item id="safe_browsing_feedback" hash_code="44583821" type="0" content_hash_code="114076664" os_list="linux,windows" file_path="chrome/browser/safe_browsing/download_protection/download_feedback.cc"/>
- <item id="safe_browsing_v4_update" hash_code="75153841" type="0" content_hash_code="112049516" os_list="linux,windows" file_path="components/safe_browsing/db/v4_update_protocol_manager.cc"/>
+ <item id="safe_browsing_g4_update" hash_code="75153841" type="0" content_hash_code="112049516" os_list="linux,windows" file_path="components/safe_browsing/db/v4_update_protocol_manager.cc"/>
  <item id="safe_browsing_incident" hash_code="124950347" type="0" content_hash_code="58481082" os_list="linux,windows" file_path="chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.cc"/>
  <item id="safe_browsing_module_loader" hash_code="6019475" type="0" content_hash_code="49511650" os_list="linux,windows" file_path="chrome/browser/safe_browsing/client_side_model_loader.cc"/>
  <item id="safe_browsing_v4_get_hash" hash_code="8561691" type="0" content_hash_code="132435617" os_list="linux,windows" file_path="components/safe_browsing/db/v4_get_hash_protocol_manager.cc"/>
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 4cf2f13..7b13246 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -20,6 +20,8 @@
 
 namespace ui {
 
+const base::char16 AXPlatformNodeBase::kEmbeddedCharacter = L'\xfffc';
+
 void AXPlatformNodeBase::Init(AXPlatformNodeDelegate* delegate) {
   delegate_ = delegate;
 }
@@ -901,6 +903,56 @@
   AddAttributeToList(name, value.c_str(), attributes);
 }
 
+AXHypertext::AXHypertext() {}
+AXHypertext::AXHypertext(const AXHypertext& other) = default;
+AXHypertext::~AXHypertext() {}
+
+AXHypertext AXPlatformNodeBase::ComputeHypertext() {
+  AXHypertext result;
+
+  if (IsPlainTextField()) {
+    result.hypertext = GetValue();
+    return result;
+  }
+
+  int child_count = delegate_->GetChildCount();
+
+  if (!child_count) {
+    if (IsRichTextField()) {
+      // We don't want to expose any associated label in IA2 Hypertext.
+      return result;
+    }
+    result.hypertext = GetString16Attribute(ax::mojom::StringAttribute::kName);
+    return result;
+  }
+
+  // Construct the hypertext for this node, which contains the concatenation
+  // of all of the static text and widespace of this node's children and an
+  // embedded object character for all the other children. Build up a map from
+  // the character index of each embedded object character to the id of the
+  // child object it points to.
+  base::string16 hypertext;
+  for (int i = 0; i < child_count; ++i) {
+    auto* child = FromNativeViewAccessible(delegate_->ChildAtIndex(i));
+
+    DCHECK(child);
+    // Similar to Firefox, we don't expose text-only objects in IA2 hypertext.
+    if (child->IsTextOnlyObject()) {
+      hypertext +=
+          child->GetString16Attribute(ax::mojom::StringAttribute::kName);
+    } else {
+      int32_t char_offset = static_cast<int32_t>(hypertext.size());
+      int32_t child_unique_id = child->GetUniqueId();
+      int32_t index = static_cast<int32_t>(result.hyperlinks.size());
+      result.hyperlink_offset_to_index[char_offset] = index;
+      result.hyperlinks.push_back(child_unique_id);
+      hypertext += kEmbeddedCharacter;
+    }
+  }
+  result.hypertext = hypertext;
+  return result;
+}
+
 // static
 void AXPlatformNodeBase::SanitizeStringAttribute(const std::string& input,
                                                  std::string* output) {
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 122c525..10d6b1c 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -5,6 +5,7 @@
 #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_BASE_H_
 #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_BASE_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -24,6 +25,24 @@
 struct AXNodeData;
 class AXPlatformNodeDelegate;
 
+struct AX_EXPORT AXHypertext {
+  AXHypertext();
+  AXHypertext(const AXHypertext& other);
+  ~AXHypertext();
+
+  // Maps an embedded character offset in |hypertext| to an index in
+  // |hyperlinks|.
+  std::map<int32_t, int32_t> hyperlink_offset_to_index;
+
+  // The unique id of a AXPlatformNodes for each hyperlink.
+  // TODO(nektar): Replace object IDs with child indices if we decide that
+  // we are not implementing IA2 hyperlinks for anything other than IA2
+  // Hypertext.
+  std::vector<int32_t> hyperlinks;
+
+  base::string16 hypertext;
+};
+
 class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode {
  public:
   virtual void Init(AXPlatformNodeDelegate* delegate);
@@ -161,6 +180,12 @@
 
   virtual base::string16 GetValue();
 
+  // Represents a non-static text node in IAccessibleHypertext (and ATK in the
+  // future). This character is embedded in the response to
+  // IAccessibleText::get_text, indicating the position where a non-static text
+  // child object appears.
+  static const base::char16 kEmbeddedCharacter;
+
   //
   // Delegate.  This is a weak reference which owns |this|.
   //
@@ -244,6 +269,11 @@
   static void SanitizeStringAttribute(const std::string& input,
                                       std::string* output);
 
+  // Compute the hypertext for this node to be exposed via IA2 (and ATK
+  // in the future). This method is responsible for properly embedding
+  // children using the special embedded element character.
+  AXHypertext ComputeHypertext();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeBase);
 };
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index c827d73..acbea7d3 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -211,10 +211,6 @@
 IAccessible2UsageObserver::~IAccessible2UsageObserver() {
 }
 
-AXHypertext::AXHypertext() {}
-AXHypertext::AXHypertext(const AXHypertext& other) = default;
-AXHypertext::~AXHypertext() {}
-
 // static
 base::ObserverList<IAccessible2UsageObserver>::Unchecked&
 GetIAccessible2UsageObserverList() {
@@ -282,8 +278,6 @@
   return g_unique_id_map.Get().size();
 }
 
-const base::char16 AXPlatformNodeWin::kEmbeddedCharacter = L'\xfffc';
-
 void AXPlatformNodeWin::ClearOwnRelations() {
   for (size_t i = 0; i < relations_.size(); ++i)
     relations_[i]->Invalidate();
@@ -5366,53 +5360,6 @@
   return value;
 }
 
-AXHypertext AXPlatformNodeWin::ComputeHypertext() {
-  AXHypertext result;
-
-  if (IsPlainTextField()) {
-    result.hypertext = GetValue();
-    return result;
-  }
-
-  int child_count = delegate_->GetChildCount();
-
-  if (!child_count) {
-    if (IsRichTextField()) {
-      // We don't want to expose any associated label in IA2 Hypertext.
-      return result;
-    }
-    result.hypertext = GetString16Attribute(ax::mojom::StringAttribute::kName);
-    return result;
-  }
-
-  // Construct the hypertext for this node, which contains the concatenation
-  // of all of the static text and widespace of this node's children and an
-  // embedded object character for all the other children. Build up a map from
-  // the character index of each embedded object character to the id of the
-  // child object it points to.
-  base::string16 hypertext;
-  for (int i = 0; i < child_count; ++i) {
-    auto* child = static_cast<AXPlatformNodeWin*>(
-        FromNativeViewAccessible(delegate_->ChildAtIndex(i)));
-
-    DCHECK(child);
-    // Similar to Firefox, we don't expose text-only objects in IA2 hypertext.
-    if (child->IsTextOnlyObject()) {
-      hypertext +=
-          child->GetString16Attribute(ax::mojom::StringAttribute::kName);
-    } else {
-      int32_t char_offset = static_cast<int32_t>(hypertext.size());
-      int32_t child_unique_id = child->GetUniqueId();
-      int32_t index = static_cast<int32_t>(result.hyperlinks.size());
-      result.hyperlink_offset_to_index[char_offset] = index;
-      result.hyperlinks.push_back(child_unique_id);
-      hypertext += kEmbeddedCharacter;
-    }
-  }
-  result.hypertext = hypertext;
-  return result;
-}
-
 bool AXPlatformNodeWin::ShouldNodeHaveReadonlyStateByDefault(
     const AXNodeData& data) const {
   switch (data.role) {
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index 2dc78f9..451b4c4 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -209,24 +209,6 @@
   virtual void OnIAccessible2Used() = 0;
 };
 
-struct AX_EXPORT AXHypertext {
-  AXHypertext();
-  AXHypertext(const AXHypertext& other);
-  ~AXHypertext();
-
-  // Maps an embedded character offset in |hypertext| to an index in
-  // |hyperlinks|.
-  std::map<int32_t, int32_t> hyperlink_offset_to_index;
-
-  // The unique id of a AXPlatformNodes for each hyperlink.
-  // TODO(nektar): Replace object IDs with child indices if we decide that
-  // we are not implementing IA2 hyperlinks for anything other than IA2
-  // Hypertext.
-  std::vector<int32_t> hyperlinks;
-
-  base::string16 hypertext;
-};
-
 // Get an observer list that allows modules across the codebase to
 // listen to when usage of IAccessible2 is detected.
 extern AX_EXPORT base::ObserverList<IAccessible2UsageObserver>::Unchecked&
@@ -292,11 +274,6 @@
 
   void Init(AXPlatformNodeDelegate* delegate) override;
 
-  // Represents a non-static text node in IAccessibleHypertext. This character
-  // is embedded in the response to IAccessibleText::get_text, indicating the
-  // position where a non-static text child object appears.
-  static const base::char16 kEmbeddedCharacter;
-
   // Clear any AXPlatformRelationWin nodes owned by this node.
   void ClearOwnRelations();
   static AXPlatformNode* GetFromUniqueId(int32_t unique_id);
@@ -858,8 +835,6 @@
 
   LONG ComputeUIAControlType();
 
-  AXHypertext ComputeHypertext();
-
   // AXPlatformNodeBase overrides.
   void Dispose() override;
 
diff --git a/ui/aura/client/default_capture_client.cc b/ui/aura/client/default_capture_client.cc
index 5132431..498407be 100644
--- a/ui/aura/client/default_capture_client.cc
+++ b/ui/aura/client/default_capture_client.cc
@@ -5,6 +5,7 @@
 #include "ui/aura/client/default_capture_client.h"
 
 #include "ui/aura/client/capture_client_observer.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
@@ -35,7 +36,7 @@
   if (capture_window_ == window)
     return;
   if (window)
-    ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(window);
+    window->env()->gesture_recognizer()->CancelActiveTouchesExcept(window);
 
   Window* old_capture_window = capture_window_;
   capture_window_ = window;
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index 1263449..d55f03a6 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -23,6 +23,7 @@
 #include "ui/aura/window_port_for_shutdown.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/events/event_target_iterator.h"
+#include "ui/events/gestures/gesture_recognizer_impl.h"
 #include "ui/events/platform/platform_event_source.h"
 
 #if defined(USE_OZONE)
@@ -207,6 +208,7 @@
       mouse_button_flags_(0),
       is_touch_down_(false),
       get_last_mouse_location_from_mus_(mode_ == Mode::MUS),
+      gesture_recognizer_(std::make_unique<ui::GestureRecognizerImpl>()),
       input_state_lookup_(InputStateLookup::Create()),
       context_factory_(nullptr),
       context_factory_private_(nullptr) {}
diff --git a/ui/aura/env.h b/ui/aura/env.h
index 55561e95..93090d7 100644
--- a/ui/aura/env.h
+++ b/ui/aura/env.h
@@ -36,6 +36,7 @@
 namespace ui {
 class ContextFactory;
 class ContextFactoryPrivate;
+class GestureRecognizer;
 class PlatformEventSource;
 namespace mojom {
 class WindowTreeClient;
@@ -172,6 +173,10 @@
     return context_factory_private_;
   }
 
+  ui::GestureRecognizer* gesture_recognizer() {
+    return gesture_recognizer_.get();
+  }
+
   // See CreateInstance() for description.
   void SetWindowTreeClient(WindowTreeClient* window_tree_client);
   bool HasWindowTreeClient() const { return window_tree_client_ != nullptr; }
@@ -258,6 +263,8 @@
   // Whether we set ourselves as the SystemInputInjectorFactory.
   bool is_override_input_injector_factory_ = false;
 
+  std::unique_ptr<ui::GestureRecognizer> gesture_recognizer_;
+
   std::unique_ptr<InputStateLookup> input_state_lookup_;
   std::unique_ptr<ui::PlatformEventSource> event_source_;
 
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc
index 39c305e..86b08db 100644
--- a/ui/aura/gestures/gesture_recognizer_unittest.cc
+++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -28,7 +28,7 @@
 #include "ui/events/event_switches.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
-#include "ui/events/gestures/gesture_recognizer_impl.h"
+#include "ui/events/gesture_detection/gesture_provider.h"
 #include "ui/events/gestures/gesture_types.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/events/test/events_test_utils.h"
@@ -346,7 +346,7 @@
   void OnTouchEvent(ui::TouchEvent* event) override {
     event->DisableSynchronousHandling();
     if (synchronous_ack_for_next_event_ != AckState::PENDING) {
-      ui::GestureRecognizer::Get()->AckTouchEvent(
+      aura::Env::GetInstance()->gesture_recognizer()->AckTouchEvent(
           event->unique_event_id(),
           synchronous_ack_for_next_event_ == AckState::CONSUMED
               ? ui::ER_CONSUMED
@@ -464,26 +464,6 @@
   DISALLOW_COPY_AND_ASSIGN(GestureEventSynthDelegate);
 };
 
-class ScopedGestureRecognizerSetter {
- public:
-  // Takes ownership of |new_gr|.
-  explicit ScopedGestureRecognizerSetter(ui::GestureRecognizer* new_gr)
-      : new_gr_(new_gr) {
-    original_gr_ = ui::GestureRecognizer::Get();
-    ui::SetGestureRecognizerForTesting(new_gr_.get());
-  }
-
-  virtual ~ScopedGestureRecognizerSetter() {
-    ui::SetGestureRecognizerForTesting(original_gr_);
-  }
-
- private:
-  ui::GestureRecognizer* original_gr_;
-  std::unique_ptr<ui::GestureRecognizer> new_gr_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedGestureRecognizerSetter);
-};
-
 class TimedEvents {
  private:
   base::SimpleTestTickClock tick_clock_;
@@ -711,7 +691,8 @@
   DispatchEventUsingWindowDispatcher(&press);
 
   // Cancel event, verify there is no crash.
-  ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
+  aura::Env::GetInstance()->gesture_recognizer()->CancelActiveTouchesExcept(
+      nullptr);
 
   EXPECT_EQ(1, handler->touch_cancelled_count());
   EXPECT_EQ(nullptr, window->parent());
@@ -2220,9 +2201,9 @@
 // Check that a touch is locked to the window of the closest current touch
 // within max_separation_for_gesture_touches_in_pixels
 TEST_F(GestureRecognizerTest, GestureEventTouchLockSelectsCorrectWindow) {
-  ui::GestureRecognizer* gesture_recognizer = new ui::GestureRecognizerImpl();
+  ui::GestureRecognizer* gesture_recognizer =
+      aura::Env::GetInstance()->gesture_recognizer();
   TimedEvents tes;
-  ScopedGestureRecognizerSetter gr_setter(gesture_recognizer);
 
   ui::GestureConsumer* target;
   const int kNumWindows = 4;
@@ -2349,8 +2330,10 @@
   // The second press should not have been locked to the same target as the
   // first, as they occured on different displays.
   EXPECT_NE(
-      ui::GestureRecognizer::Get()->GetTouchLockedTarget(press1),
-      ui::GestureRecognizer::Get()->GetTouchLockedTarget(press2));
+      aura::Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(
+          press1),
+      aura::Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(
+          press2));
 }
 
 // Check that touch events outside the root window are still handled
@@ -2374,12 +2357,16 @@
 
   // As these presses were outside the root window, they should be
   // associated with the root window.
-  EXPECT_EQ(root_window(),
-            static_cast<aura::Window*>(
-                ui::GestureRecognizer::Get()->GetTouchLockedTarget(press1)));
-  EXPECT_EQ(root_window(),
-            static_cast<aura::Window*>(
-                ui::GestureRecognizer::Get()->GetTouchLockedTarget(press2)));
+  EXPECT_EQ(
+      root_window(),
+      static_cast<aura::Window*>(
+          aura::Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(
+              press1)));
+  EXPECT_EQ(
+      root_window(),
+      static_cast<aura::Window*>(
+          aura::Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(
+              press2)));
 }
 
 TEST_F(GestureRecognizerTest, NoTapWithPreventDefaultedRelease) {
@@ -3116,10 +3103,14 @@
       ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, kTouchId2));
   DispatchEventUsingWindowDispatcher(&press2);
   window->Hide();
-  EXPECT_EQ(NULL,
-      ui::GestureRecognizer::Get()->GetTouchLockedTarget(press1));
-  EXPECT_EQ(NULL,
-      ui::GestureRecognizer::Get()->GetTouchLockedTarget(press2));
+  EXPECT_EQ(
+      NULL,
+      aura::Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(
+          press1));
+  EXPECT_EQ(
+      NULL,
+      aura::Env::GetInstance()->gesture_recognizer()->GetTouchLockedTarget(
+          press2));
 }
 
 TEST_F(GestureRecognizerTest, LongPressTimerStopsOnPreventDefaultedTouchMoves) {
@@ -3805,11 +3796,13 @@
   delegate->Reset();
   handler->Reset();
 
-  ui::GestureRecognizer* gesture_recognizer = ui::GestureRecognizer::Get();
+  ui::GestureRecognizer* gesture_recognizer =
+      aura::Env::GetInstance()->gesture_recognizer();
   EXPECT_EQ(window.get(),
             gesture_recognizer->GetTouchLockedTarget(press));
 
-  ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
+  aura::Env::GetInstance()->gesture_recognizer()->CancelActiveTouchesExcept(
+      nullptr);
 
   EXPECT_EQ(NULL, gesture_recognizer->GetTouchLockedTarget(press));
   EXPECT_4_EVENTS(delegate->events(),
@@ -4304,7 +4297,6 @@
     GestureEventConsumeDelegate::OnGestureEvent(gesture);
     if (gesture->type() != ui::ET_GESTURE_LONG_PRESS)
       return;
-    ui::GestureRecognizer::Get()->CleanupStateForConsumer(*window_);
     delete *window_;
     *window_ = NULL;
   }
@@ -4712,7 +4704,7 @@
   delegate_2->set_window(window_2.get());
 
   // Transfer event sequence from previous window to the new window.
-  ui::GestureRecognizer::Get()->TransferEventsTo(
+  aura::Env::GetInstance()->gesture_recognizer()->TransferEventsTo(
       window_1.get(), window_2.get(),
       ui::GestureRecognizer::ShouldCancelTouches::DontCancel);
 
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index 8184e97..38fa90f 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -192,7 +192,6 @@
   focus_client_.reset();
   capture_client_.reset();
 
-  ui::GestureRecognizer::Reset();
   ui::ShutdownInputMethodForTesting();
 
   if (env_) {
diff --git a/ui/aura/test/env_test_helper.h b/ui/aura/test/env_test_helper.h
index a696acf..10af034 100644
--- a/ui/aura/test/env_test_helper.h
+++ b/ui/aura/test/env_test_helper.h
@@ -11,6 +11,7 @@
 #include "ui/aura/env.h"
 #include "ui/aura/env_input_state_controller.h"
 #include "ui/aura/input_state_lookup.h"
+#include "ui/events/gestures/gesture_recognizer.h"
 
 namespace aura {
 namespace test {
@@ -63,6 +64,11 @@
     env_->always_use_last_mouse_location_ = value;
   }
 
+  void SetGestureRecognizer(
+      std::unique_ptr<ui::GestureRecognizer> gesture_recognizer) {
+    env_->gesture_recognizer_ = std::move(gesture_recognizer);
+  }
+
   WindowTreeClient* GetWindowTreeClient() { return env_->window_tree_client_; }
 
  private:
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index 043590a9..be708fc 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -1086,9 +1086,8 @@
 
 bool Window::CleanupGestureState() {
   bool state_modified = false;
-  state_modified |= ui::GestureRecognizer::Get()->CancelActiveTouches(this);
-  state_modified |=
-      ui::GestureRecognizer::Get()->CleanupStateForConsumer(this);
+  state_modified |= env_->gesture_recognizer()->CancelActiveTouches(this);
+  state_modified |= env_->gesture_recognizer()->CleanupStateForConsumer(this);
   for (Window::Windows::iterator iter = children_.begin();
        iter != children_.end();
        ++iter) {
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index 547cceb..2473091 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -105,7 +105,7 @@
       are_events_in_pixels_(are_events_in_pixels),
       observer_manager_(this),
       event_targeter_(std::make_unique<WindowTargeter>()) {
-  ui::GestureRecognizer::Get()->AddGestureEventHelper(this);
+  env_->gesture_recognizer()->AddGestureEventHelper(this);
   env_->AddObserver(this);
   if (env_->mode() == Env::Mode::MUS)
     mus_mouse_location_updater_ = std::make_unique<MusMouseLocationUpdater>();
@@ -113,8 +113,8 @@
 
 WindowEventDispatcher::~WindowEventDispatcher() {
   TRACE_EVENT0("shutdown", "WindowEventDispatcher::Destructor");
+  env_->gesture_recognizer()->RemoveGestureEventHelper(this);
   env_->RemoveObserver(this);
-  ui::GestureRecognizer::Get()->RemoveGestureEventHelper(this);
 }
 
 void WindowEventDispatcher::Shutdown() {
@@ -201,7 +201,7 @@
     ui::EventResult result,
     bool is_source_touch_event_set_non_blocking) {
   ui::GestureRecognizer::Gestures gestures =
-      ui::GestureRecognizer::Get()->AckTouchEvent(
+      env_->gesture_recognizer()->AckTouchEvent(
           unique_event_id, result, is_source_touch_event_set_non_blocking,
           window);
   DispatchDetails details = ProcessGestures(window, std::move(gestures));
@@ -647,7 +647,7 @@
       if (!touchevent.synchronous_handling_disabled()) {
         Window* window = static_cast<Window*>(target);
         ui::GestureRecognizer::Gestures gestures =
-            ui::GestureRecognizer::Get()->AckTouchEvent(
+            env_->gesture_recognizer()->AckTouchEvent(
                 touchevent.unique_event_id(), event.result(),
                 false /* is_source_touch_event_set_non_blocking */, window);
 
@@ -1068,8 +1068,8 @@
   host_->window()->env()->env_controller()->UpdateStateForTouchEvent(*event);
 
   ui::TouchEvent orig_event(*event, target, window());
-  if (!ui::GestureRecognizer::Get()->ProcessTouchEventPreDispatch(&orig_event,
-                                                                  target)) {
+  if (!env_->gesture_recognizer()->ProcessTouchEventPreDispatch(&orig_event,
+                                                                target)) {
     // The event is invalid - ignore it.
     event->StopPropagation();
     event->DisableSynchronousHandling();
diff --git a/ui/aura/window_targeter.cc b/ui/aura/window_targeter.cc
index 7703af7..824159b 100644
--- a/ui/aura/window_targeter.cc
+++ b/ui/aura/window_targeter.cc
@@ -9,6 +9,7 @@
 #include "ui/aura/client/event_client.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -96,7 +97,7 @@
     // Query the gesture-recognizer to find targets for touch events.
     const ui::TouchEvent& touch = *event.AsTouchEvent();
     ui::GestureConsumer* consumer =
-        ui::GestureRecognizer::Get()->GetTouchLockedTarget(touch);
+        root_window->env()->gesture_recognizer()->GetTouchLockedTarget(touch);
     if (consumer)
       return static_cast<Window*>(consumer);
   }
@@ -116,10 +117,11 @@
     // Query the gesture-recognizer to find targets for touch events.
     const ui::TouchEvent& touch = *event.AsTouchEvent();
     // GetTouchLockedTarget() is handled in GetPriorityTargetInRootWindow().
-    DCHECK(!ui::GestureRecognizer::Get()->GetTouchLockedTarget(touch));
-    ui::GestureConsumer* consumer =
-        ui::GestureRecognizer::Get()->GetTargetForLocation(
-            event.location_f(), touch.source_device_id());
+    ui::GestureRecognizer* gesture_recognizer =
+        root_window->env()->gesture_recognizer();
+    DCHECK(!gesture_recognizer->GetTouchLockedTarget(touch));
+    ui::GestureConsumer* consumer = gesture_recognizer->GetTargetForLocation(
+        event.location_f(), touch.source_device_id());
     if (consumer)
       return static_cast<Window*>(consumer);
 
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index dd0c77d..25b3dc3 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -182,6 +182,7 @@
     "events_export.h",
     "events_stub.cc",
     "gestures/gesture_recognizer_impl_mac.cc",
+    "gestures/gesture_recognizer_impl_mac.h",
     "gestures/gesture_types.cc",
     "gestures/gesture_types.h",
     "keyboard_hook.h",
diff --git a/ui/events/gestures/gesture_recognizer.h b/ui/events/gestures/gesture_recognizer.h
index 8e24e6d7..f4da6a0 100644
--- a/ui/events/gestures/gesture_recognizer.h
+++ b/ui/events/gestures/gesture_recognizer.h
@@ -20,10 +20,6 @@
 // into gestures.
 class EVENTS_EXPORT GestureRecognizer {
  public:
-  static GestureRecognizer* Create();
-  static GestureRecognizer* Get();
-  static void Reset();
-
   using Gestures = std::vector<std::unique_ptr<GestureEvent>>;
 
   virtual ~GestureRecognizer() {}
diff --git a/ui/events/gestures/gesture_recognizer_impl.cc b/ui/events/gestures/gesture_recognizer_impl.cc
index d7ee5ee..1702be9 100644
--- a/ui/events/gestures/gesture_recognizer_impl.cc
+++ b/ui/events/gestures/gesture_recognizer_impl.cc
@@ -362,37 +362,4 @@
   return NULL;
 }
 
-// GestureRecognizer, static
-GestureRecognizer* GestureRecognizer::Create() {
-  return new GestureRecognizerImpl();
-}
-
-static GestureRecognizerImpl* g_gesture_recognizer_instance = NULL;
-
-// GestureRecognizer, static
-GestureRecognizer* GestureRecognizer::Get() {
-  if (!g_gesture_recognizer_instance)
-    g_gesture_recognizer_instance = new GestureRecognizerImpl();
-  return g_gesture_recognizer_instance;
-}
-
-// GestureRecognizer, static
-void GestureRecognizer::Reset() {
-  delete g_gesture_recognizer_instance;
-  g_gesture_recognizer_instance = NULL;
-}
-
-void SetGestureRecognizerForTesting(GestureRecognizer* gesture_recognizer) {
-  // Transfer helpers to the new GR.
-  std::vector<GestureEventHelper*>& helpers =
-      g_gesture_recognizer_instance->helpers();
-  std::vector<GestureEventHelper*>::iterator it;
-  for (it = helpers.begin(); it != helpers.end(); ++it)
-    gesture_recognizer->AddGestureEventHelper(*it);
-
-  helpers.clear();
-  g_gesture_recognizer_instance =
-      static_cast<GestureRecognizerImpl*>(gesture_recognizer);
-}
-
 }  // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer_impl.h b/ui/events/gestures/gesture_recognizer_impl.h
index 52901ce..ba6c416 100644
--- a/ui/events/gestures/gesture_recognizer_impl.h
+++ b/ui/events/gestures/gesture_recognizer_impl.h
@@ -105,10 +105,6 @@
   DISALLOW_COPY_AND_ASSIGN(GestureRecognizerImpl);
 };
 
-// Provided only for testing:
-EVENTS_EXPORT void SetGestureRecognizerForTesting(
-    GestureRecognizer* gesture_recognizer);
-
 }  // namespace ui
 
 #endif  // UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_IMPL_H_
diff --git a/ui/events/gestures/gesture_recognizer_impl_mac.cc b/ui/events/gestures/gesture_recognizer_impl_mac.cc
index 359b909b..b52a8db 100644
--- a/ui/events/gestures/gesture_recognizer_impl_mac.cc
+++ b/ui/events/gestures/gesture_recognizer_impl_mac.cc
@@ -2,66 +2,65 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "ui/events/event.h"
-#include "ui/events/gestures/gesture_recognizer.h"
+#include "ui/events/gestures/gesture_recognizer_impl_mac.h"
 
 namespace ui {
 
-namespace {
+GestureRecognizerImplMac::GestureRecognizerImplMac() = default;
+GestureRecognizerImplMac::~GestureRecognizerImplMac() = default;
 
-// Stub implementation of GestureRecognizer for Mac. Currently only serves to
-// provide a no-op implementation of TransferEventsTo().
-class GestureRecognizerImplMac : public GestureRecognizer {
- public:
-  GestureRecognizerImplMac() {}
-  ~GestureRecognizerImplMac() override {}
-
- private:
-  bool ProcessTouchEventPreDispatch(TouchEvent* event,
-                                    GestureConsumer* consumer) override {
-    return false;
-  }
-
-  Gestures AckTouchEvent(uint32_t unique_event_id,
-                         ui::EventResult result,
-                         bool is_source_touch_event_set_non_blocking,
-                         GestureConsumer* consumer) override {
-    return {};
-  }
-  bool CleanupStateForConsumer(GestureConsumer* consumer) override {
-    return false;
-  }
-  GestureConsumer* GetTouchLockedTarget(const TouchEvent& event) override {
-    return NULL;
-  }
-  GestureConsumer* GetTargetForLocation(const gfx::PointF& location,
-                                        int source_device_id) override {
-    return NULL;
-  }
-  void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) override {}
-  void TransferEventsTo(GestureConsumer* current_consumer,
-                        GestureConsumer* new_consumer,
-                        ShouldCancelTouches should_cancel_touches) override {}
-  bool GetLastTouchPointForTarget(GestureConsumer* consumer,
-                                  gfx::PointF* point) override {
-    return false;
-  }
-  bool CancelActiveTouches(GestureConsumer* consumer) override { return false; }
-  void AddGestureEventHelper(GestureEventHelper* helper) override {}
-  void RemoveGestureEventHelper(GestureEventHelper* helper) override {}
-
-  DISALLOW_COPY_AND_ASSIGN(GestureRecognizerImplMac);
-};
-
-}  // namespace
-
-// static
-GestureRecognizer* GestureRecognizer::Get() {
-  CR_DEFINE_STATIC_LOCAL(GestureRecognizerImplMac, instance, ());
-  return &instance;
+bool GestureRecognizerImplMac::ProcessTouchEventPreDispatch(
+    TouchEvent* event,
+    GestureConsumer* consumer) {
+  return false;
 }
 
+GestureRecognizer::Gestures GestureRecognizerImplMac::AckTouchEvent(
+    uint32_t unique_event_id,
+    ui::EventResult result,
+    bool is_source_touch_event_set_non_blocking,
+    GestureConsumer* consumer) {
+  return {};
+}
+
+bool GestureRecognizerImplMac::CleanupStateForConsumer(
+    GestureConsumer* consumer) {
+  return false;
+}
+
+GestureConsumer* GestureRecognizerImplMac::GetTouchLockedTarget(
+    const TouchEvent& event) {
+  return NULL;
+}
+
+GestureConsumer* GestureRecognizerImplMac::GetTargetForLocation(
+    const gfx::PointF& location,
+    int source_device_id) {
+  return NULL;
+}
+
+void GestureRecognizerImplMac::CancelActiveTouchesExcept(
+    GestureConsumer* not_cancelled) {}
+
+void GestureRecognizerImplMac::TransferEventsTo(
+    GestureConsumer* current_consumer,
+    GestureConsumer* new_consumer,
+    ShouldCancelTouches should_cancel_touches) {}
+
+bool GestureRecognizerImplMac::GetLastTouchPointForTarget(
+    GestureConsumer* consumer,
+    gfx::PointF* point) {
+  return false;
+}
+
+bool GestureRecognizerImplMac::CancelActiveTouches(GestureConsumer* consumer) {
+  return false;
+}
+
+void GestureRecognizerImplMac::AddGestureEventHelper(
+    GestureEventHelper* helper) {}
+
+void GestureRecognizerImplMac::RemoveGestureEventHelper(
+    GestureEventHelper* helper) {}
+
 }  // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer_impl_mac.h b/ui/events/gestures/gesture_recognizer_impl_mac.h
new file mode 100644
index 0000000..391bd2b
--- /dev/null
+++ b/ui/events/gestures/gesture_recognizer_impl_mac.h
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_IMPL_MAC_H_
+#define UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_IMPL_MAC_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "ui/events/event.h"
+#include "ui/events/events_export.h"
+#include "ui/events/gestures/gesture_recognizer.h"
+
+namespace ui {
+
+// Stub implementation of GestureRecognizer for Mac. Currently only serves to
+// provide a no-op implementation of TransferEventsTo().
+class EVENTS_EXPORT GestureRecognizerImplMac : public GestureRecognizer {
+ public:
+  GestureRecognizerImplMac();
+  ~GestureRecognizerImplMac() override;
+
+ private:
+  // GestureRecognizer:
+  bool ProcessTouchEventPreDispatch(TouchEvent* event,
+                                    GestureConsumer* consumer) override;
+  Gestures AckTouchEvent(uint32_t unique_event_id,
+                         ui::EventResult result,
+                         bool is_source_touch_event_set_non_blocking,
+                         GestureConsumer* consumer) override;
+  bool CleanupStateForConsumer(GestureConsumer* consumer) override;
+  GestureConsumer* GetTouchLockedTarget(const TouchEvent& event) override;
+  GestureConsumer* GetTargetForLocation(const gfx::PointF& location,
+                                        int source_device_id) override;
+  void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) override;
+  void TransferEventsTo(GestureConsumer* current_consumer,
+                        GestureConsumer* new_consumer,
+                        ShouldCancelTouches should_cancel_touches) override;
+  bool GetLastTouchPointForTarget(GestureConsumer* consumer,
+                                  gfx::PointF* point) override;
+  bool CancelActiveTouches(GestureConsumer* consumer) override;
+  void AddGestureEventHelper(GestureEventHelper* helper) override;
+  void RemoveGestureEventHelper(GestureEventHelper* helper) override;
+
+  DISALLOW_COPY_AND_ASSIGN(GestureRecognizerImplMac);
+};
+
+}  // namespace ui
+
+#endif  // UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_IMPL_MAC_H_
diff --git a/ui/file_manager/integration_tests/file_manager/create_new_folder.js b/ui/file_manager/integration_tests/file_manager/create_new_folder.js
index fe57833..808bf157 100644
--- a/ui/file_manager/integration_tests/file_manager/create_new_folder.js
+++ b/ui/file_manager/integration_tests/file_manager/create_new_folder.js
@@ -5,44 +5,37 @@
 'use strict';
 
 /**
- * Constants for interacting with the directory tree on the LHS of Files.
+ * Constants for interacting with the directory tree on the LHS of Files app.
  * When we are not in guest mode, we fill Google Drive with the basic entry set
  * which causes an extra tree-item to be added.
  */
 var TREEITEM_DRIVE = '#directory-tree [entry-label="My Drive"] ';
-var TREEITEM_DOWNLOADS =
-    '#directory-tree [volume-type-for-testing="downloads"] ';
-var EXPAND_ICON = '> .tree-row > .expand-icon';
-var EXPANDED_SUBTREE = '> .tree-children[expanded]';
+var TREEITEM_DOWNLOADS = '#directory-tree [entry-label="Downloads"] ';
 
 /**
  * Selects the first item in the file list.
- * @param {string} windowId ID of the target window.
+ *
+ * @param {string} windowId The Files app windowId.
  * @return {Promise} Promise to be fulfilled on success.
  */
-function selectFirstListItem(windowId) {
+function selectFirstFileListItem(windowId) {
   return Promise.resolve().then(function() {
-    // Ensure no selected item.
-    return remoteCall.waitForElementLost(
-        windowId,
-        'div.detail-table > list > li[selected]');
+    // Ensure no file list items are selected.
+    return remoteCall.waitForElementLost(windowId, ['#file-list [selected]']);
   }).then(function() {
-    // Push Down.
+    // Press DownArrow key to select an item.
+    const key = ['#file-list', 'ArrowDown', 'Down', false, false, false];
+    return remoteCall.callRemoteTestUtil('fakeKeyDown', windowId, key);
+  }).then(function(result) {
+    chrome.test.assertTrue(result);
+    // Await file list item selection.
+    return remoteCall.waitForElement(windowId, ['.table-row[selected]']);
+  }).then(function() {
+    // Retrieve all selected items in the file list.
     return remoteCall.callRemoteTestUtil(
-        'fakeKeyDown', windowId,
-        // Down
-        ['#file-list', 'ArrowDown', 'Down', true, false, false]);
-  }).then(function() {
-    // Wait for selection.
-    return remoteCall.waitForElement(windowId,
-                                     'div.detail-table > list > li[selected]');
-  }).then(function() {
-    // Ensure that only the first item is selected.
-    return remoteCall.callRemoteTestUtil(
-        'queryAllElements',
-        windowId,
-        ['div.detail-table > list > li[selected]']);
+        'queryAllElements', windowId, ['#file-list [selected]']);
   }).then(function(elements) {
+    // Check: the first list item only should be selected.
     chrome.test.assertEq(1, elements.length);
     chrome.test.assertEq('listitem-1', elements[0].attributes['id']);
   });
@@ -50,7 +43,7 @@
 
 /**
  * Creates new folder.
- * @param {string} windowId ID of the target window.
+ * @param {string} windowId The Files app windowId.
  * @param {string} path Initial path.
  * @param {Array<TestEntryInfo>} initialEntrySet Initial set of entries.
  * @param {string} rootLabel label path's root.
@@ -139,35 +132,45 @@
 }
 
 /**
- * This is used to expand the tree item for Downloads or Drive.
- * @param {string} windowId The Files app window.
- * @param {string} selector The Downloads or Drive tree item selector.
+ * Expands the directory tree item given by |selector| (Downloads or Drive)
+ * to reveal its subtree child items.
+ *
+ * @param {string} appId The Files app windowId.
+ * @param {string} selector Downloads or Drive directory tree item selector.
  * @return {Promise} Promise fulfilled on success.
  */
-function expandRoot(windowId, selector) {
-  return remoteCall.waitForElement(
-      windowId, selector + EXPAND_ICON).then(function() {
-    return remoteCall.callRemoteTestUtil(
-        'fakeMouseClick', windowId, [selector + EXPAND_ICON]);
+function expandRoot(appId, selector) {
+  const expandIcon = selector + ' > .tree-row > .expand-icon';
+
+  return new Promise(function(resolve) {
+    // Wait for the subtree expand icon to appear.
+    remoteCall.waitForElement(appId, [expandIcon]).then(resolve);
+  }).then(function() {
+    // Click the expand icon to expand the subtree.
+    return remoteCall.callRemoteTestUtil('fakeMouseClick', appId, [expandIcon]);
   }).then(function(result) {
     chrome.test.assertTrue(result);
-    return remoteCall.waitForElement(windowId,
-        selector + EXPANDED_SUBTREE);
+    // Wait for the subtree to expand and display its children.
+    const expandedSubtree = selector + ' > .tree-children[expanded]';
+    return remoteCall.waitForElement(appId, expandedSubtree);
+  }).then(function(element) {
+    // Verify expected subtree child item name.
+    if (element.text.indexOf('photos') === -1)
+      chrome.test.fail('directory subtree child item "photos" not found');
   });
 }
 
 testcase.selectCreateFolderDownloads = function() {
-  var PATH = RootPath.DOWNLOADS;
-  var windowId = null;
-  var promise = new Promise(function(callback) {
-    setupAndWaitUntilReady(null, PATH, callback);
+  let windowId;
+
+  const promise = new Promise(function(resolve) {
+    setupAndWaitUntilReady(
+        null, RootPath.DOWNLOADS, resolve, BASIC_LOCAL_ENTRY_SET, []);
   }).then(function(results) {
     windowId = results.windowId;
-    return selectFirstListItem(windowId);
-  }).then(function() {
     return expandRoot(windowId, TREEITEM_DOWNLOADS);
   }).then(function() {
-    return remoteCall.waitForElement(windowId, '#detail-table');
+    return selectFirstFileListItem(windowId);
   }).then(function() {
     return createNewFolder(windowId, '', BASIC_LOCAL_ENTRY_SET, 'Downloads');
   });
@@ -176,16 +179,15 @@
 };
 
 testcase.createFolderDownloads = function() {
-  var PATH = RootPath.DOWNLOADS;
-  var windowId = null;
-  var promise = new Promise(function(callback) {
-    setupAndWaitUntilReady(null, PATH, callback);
+  let windowId;
+
+  const promise = new Promise(function(resolve) {
+    setupAndWaitUntilReady(
+        null, RootPath.DOWNLOADS, resolve, BASIC_LOCAL_ENTRY_SET, []);
   }).then(function(results) {
     windowId = results.windowId;
     return expandRoot(windowId, TREEITEM_DOWNLOADS);
   }).then(function() {
-    return remoteCall.waitForElement(windowId, '#detail-table');
-  }).then(function() {
     return createNewFolder(windowId, '', BASIC_LOCAL_ENTRY_SET, 'Downloads');
   });
 
@@ -193,36 +195,32 @@
 };
 
 testcase.createFolderNestedDownloads = function() {
-  var PATH = RootPath.DOWNLOADS;
-  const expectedPhotosInitialSet = [];
+  let windowId;
 
-  var windowId = null;
-  var promise = new Promise(function(callback) {
-    setupAndWaitUntilReady(null, PATH, callback);
+  const promise = new Promise(function(resolve) {
+    setupAndWaitUntilReady(
+        null, RootPath.DOWNLOADS, resolve, BASIC_LOCAL_ENTRY_SET, []);
   }).then(function(results) {
     windowId = results.windowId;
     return expandRoot(windowId, TREEITEM_DOWNLOADS);
   }).then(function() {
-    return remoteCall.waitForElement(windowId, '#detail-table');
-  }).then(function() {
-    return createNewFolder(
-        windowId, '/photos', expectedPhotosInitialSet, 'Downloads');
+    const photosEntrySet = [];
+    return createNewFolder(windowId, '/photos', photosEntrySet, 'Downloads');
   });
 
   testPromise(promise);
 };
 
 testcase.createFolderDrive = function() {
-  var PATH = RootPath.DRIVE;
-  var windowId = null;
-  var promise = new Promise(function(callback) {
-    setupAndWaitUntilReady(null, PATH, callback);
+  let windowId;
+
+  const promise = new Promise(function(resolve) {
+    setupAndWaitUntilReady(
+        null, RootPath.DRIVE, resolve, [], BASIC_DRIVE_ENTRY_SET);
   }).then(function(results) {
     windowId = results.windowId;
     return expandRoot(windowId, TREEITEM_DRIVE);
   }).then(function() {
-    return remoteCall.waitForElement(windowId, '#detail-table');
-  }).then(function() {
     return createNewFolder(windowId, '', BASIC_DRIVE_ENTRY_SET, 'My Drive');
   });
 
diff --git a/ui/gfx/geometry/rect.cc b/ui/gfx/geometry/rect.cc
index 63c0bbf..dd51fd4 100644
--- a/ui/gfx/geometry/rect.cc
+++ b/ui/gfx/geometry/rect.cc
@@ -257,6 +257,10 @@
   SetRect(new_x, new_y, new_width, new_height);
 }
 
+void Rect::Transpose() {
+  SetRect(y(), x(), height(), width());
+}
+
 void Rect::SplitVertically(Rect* left_half, Rect* right_half) const {
   DCHECK(left_half);
   DCHECK(right_half);
diff --git a/ui/gfx/geometry/rect.h b/ui/gfx/geometry/rect.h
index 3109912..8a4a3f87 100644
--- a/ui/gfx/geometry/rect.h
+++ b/ui/gfx/geometry/rect.h
@@ -103,6 +103,15 @@
   constexpr Point bottom_left() const { return Point(x(), bottom()); }
   constexpr Point bottom_right() const { return Point(right(), bottom()); }
 
+  constexpr Point left_center() const { return Point(x(), y() + height() / 2); }
+  constexpr Point top_center() const { return Point(x() + width() / 2, y()); }
+  constexpr Point right_center() const {
+    return Point(right(), y() + height() / 2);
+  }
+  constexpr Point bottom_center() const {
+    return Point(x() + width() / 2, bottom());
+  }
+
   Vector2d OffsetFromOrigin() const { return Vector2d(x(), y()); }
 
   void SetRect(int x, int y, int width, int height) {
@@ -189,6 +198,9 @@
   // at given |size|.
   void ClampToCenteredSize(const Size& size);
 
+  // Transpose x and y axis.
+  void Transpose();
+
   // Splits |this| in two halves, |left_half| and |right_half|.
   void SplitVertically(Rect* left_half, Rect* right_half) const;
 
diff --git a/ui/gfx/geometry/rect_f.cc b/ui/gfx/geometry/rect_f.cc
index bb474ba6..eb65785 100644
--- a/ui/gfx/geometry/rect_f.cc
+++ b/ui/gfx/geometry/rect_f.cc
@@ -183,6 +183,10 @@
   SetRect(new_x, new_y, new_width, new_height);
 }
 
+void RectF::Transpose() {
+  SetRect(y(), x(), height(), width());
+}
+
 void RectF::SplitVertically(RectF* left_half, RectF* right_half) const {
   DCHECK(left_half);
   DCHECK(right_half);
diff --git a/ui/gfx/geometry/rect_f.h b/ui/gfx/geometry/rect_f.h
index 9d99052..7ec5c97 100644
--- a/ui/gfx/geometry/rect_f.h
+++ b/ui/gfx/geometry/rect_f.h
@@ -70,6 +70,17 @@
   constexpr PointF bottom_left() const { return PointF(x(), bottom()); }
   constexpr PointF bottom_right() const { return PointF(right(), bottom()); }
 
+  constexpr PointF left_center() const {
+    return PointF(x(), y() + height() / 2);
+  }
+  constexpr PointF top_center() const { return PointF(x() + width() / 2, y()); }
+  constexpr PointF right_center() const {
+    return PointF(right(), y() + height() / 2);
+  }
+  constexpr PointF bottom_center() const {
+    return PointF(x() + width() / 2, bottom());
+  }
+
   Vector2dF OffsetFromOrigin() const { return Vector2dF(x(), y()); }
 
   void SetRect(float x, float y, float width, float height) {
@@ -149,6 +160,9 @@
   // at given |size|.
   void ClampToCenteredSize(const SizeF& size);
 
+  // Transpose x and y axis.
+  void Transpose();
+
   // Splits |this| in two halves, |left_half| and |right_half|.
   void SplitVertically(RectF* left_half, RectF* right_half) const;
 
diff --git a/ui/gfx/geometry/rect_unittest.cc b/ui/gfx/geometry/rect_unittest.cc
index aaa533b..7acfb80 100644
--- a/ui/gfx/geometry/rect_unittest.cc
+++ b/ui/gfx/geometry/rect_unittest.cc
@@ -869,6 +869,31 @@
   EXPECT_EQ(PointF(4.2f, 6.2f), f.bottom_right());
 }
 
+TEST(RectTest, Centers) {
+  Rect i(10, 20, 30, 40);
+  EXPECT_EQ(Point(10, 40), i.left_center());
+  EXPECT_EQ(Point(25, 20), i.top_center());
+  EXPECT_EQ(Point(40, 40), i.right_center());
+  EXPECT_EQ(Point(25, 60), i.bottom_center());
+
+  RectF f(10.1f, 20.2f, 30.3f, 40.4f);
+  EXPECT_EQ(PointF(10.1f, 40.4f), f.left_center());
+  EXPECT_EQ(PointF(25.25f, 20.2f), f.top_center());
+  EXPECT_EQ(PointF(40.4f, 40.4f), f.right_center());
+  EXPECT_EQ(25.25f, f.bottom_center().x());
+  EXPECT_NEAR(60.6f, f.bottom_center().y(), 0.001f);
+}
+
+TEST(RectTest, Transpose) {
+  Rect i(10, 20, 30, 40);
+  i.Transpose();
+  EXPECT_EQ(Rect(20, 10, 40, 30), i);
+
+  RectF f(10.1f, 20.2f, 30.3f, 40.4f);
+  f.Transpose();
+  EXPECT_EQ(RectF(20.2f, 10.1f, 40.4f, 30.3f), f);
+}
+
 TEST(RectTest, ManhattanDistanceToPoint) {
   Rect i(1, 2, 3, 4);
   EXPECT_EQ(0, i.ManhattanDistanceToPoint(Point(1, 2)));
diff --git a/ui/views/bubble/tooltip_icon.cc b/ui/views/bubble/tooltip_icon.cc
index 2b88baf..8e1969cd 100644
--- a/ui/views/bubble/tooltip_icon.cc
+++ b/ui/views/bubble/tooltip_icon.cc
@@ -61,7 +61,7 @@
 
 void TooltipIcon::MouseMovedOutOfHost() {
   if (IsMouseHovered()) {
-    mouse_watcher_->Start();
+    mouse_watcher_->Start(GetWidget()->GetNativeWindow());
     return;
   }
 
@@ -96,7 +96,7 @@
     View* frame = bubble_->GetWidget()->non_client_view()->frame_view();
     mouse_watcher_ = std::make_unique<MouseWatcher>(
         std::make_unique<MouseWatcherViewHost>(frame, gfx::Insets()), this);
-    mouse_watcher_->Start();
+    mouse_watcher_->Start(GetWidget()->GetNativeWindow());
   }
 }
 
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc
index 1d769dc..9e2994cf 100644
--- a/ui/views/bubble/tray_bubble_view.cc
+++ b/ui/views/bubble/tray_bubble_view.cc
@@ -419,7 +419,7 @@
     // cannot see a lag.
     mouse_watcher_->set_notify_on_exit_time(
         base::TimeDelta::FromMilliseconds(kFrameTimeInMS));
-    mouse_watcher_->Start();
+    mouse_watcher_->Start(GetWidget()->GetNativeWindow());
   }
 }
 
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm
index ce3f37b..78e7b0b 100644
--- a/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -104,6 +104,12 @@
   NSImage* image = gfx::NSImageFromImageSkiaWithColorSpace(
       provider.GetDragImage(), base::mac::GetSRGBColorSpace());
 
+  // TODO(crbug/876201): This shouldn't happen. When a repro for this
+  // is identified and the bug is fixed, change the early return to
+  // a DCHECK.
+  if (!image || NSEqualSizes([image size], NSZeroSize))
+    return;
+
   base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
   [item setDataProvider:data_source_.get()
                forTypes:provider.GetAvailableTypes()];
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc
index 992f116..104ae4c6 100644
--- a/ui/views/controls/menu/menu_host.cc
+++ b/ui/views/controls/menu/menu_host.cc
@@ -84,7 +84,7 @@
 #if defined(OS_MACOSX)
   NOTIMPLEMENTED();
 #else   // !defined(OS_MACOSX)
-  ui::GestureRecognizer::Get()->TransferEventsTo(
+  source->GetGestureRecognizer()->TransferEventsTo(
       source->GetNativeView(), target->GetNativeView(),
       ui::GestureRecognizer::ShouldCancelTouches::DontCancel);
 #endif  // defined(OS_MACOSX)
@@ -171,12 +171,12 @@
       // gesture events instead of being dropped.
       internal::TransferGesture(owner_, this);
     } else {
-      ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
+      GetGestureRecognizer()->CancelActiveTouchesExcept(nullptr);
     }
 #if defined(MACOSX)
     // Cancel existing touches, so we don't miss some touch release/cancel
     // events due to the menu taking capture.
-    ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
+    GetGestureRecognizer()->CancelActiveTouchesExcept(nullptr);
 #endif  // defined (OS_MACOSX)
     // If MenuHost has no parent widget, it needs to call Show to get focus,
     // so that it will get keyboard events.
diff --git a/ui/views/event_monitor.h b/ui/views/event_monitor.h
index 2867ecb..d866e432 100644
--- a/ui/views/event_monitor.h
+++ b/ui/views/event_monitor.h
@@ -25,9 +25,11 @@
 
   // Create an instance for monitoring application events.
   // Events will be forwarded to |event_handler| before they are dispatched to
-  // the application.
+  // the application. |context| is used to determine where to observer events
+  // from. |context| may be destroyed before the EventMonitor.
   static std::unique_ptr<EventMonitor> CreateApplicationMonitor(
-      ui::EventHandler* event_handler);
+      ui::EventHandler* event_handler,
+      gfx::NativeWindow context);
 
   // Create an instance for monitoring events on a specific window.
   // Events will be forwarded to |event_handler| before they are dispatched to
@@ -39,7 +41,7 @@
 
   // Returns the last mouse location seen in a mouse event in screen
   // coordinates.
-  static gfx::Point GetLastMouseLocation();
+  virtual gfx::Point GetLastMouseLocation() = 0;
 };
 
 }  // namespace views
diff --git a/ui/views/event_monitor_aura.cc b/ui/views/event_monitor_aura.cc
index 6c52620..5afde17f 100644
--- a/ui/views/event_monitor_aura.cc
+++ b/ui/views/event_monitor_aura.cc
@@ -14,26 +14,25 @@
 
 // static
 std::unique_ptr<EventMonitor> EventMonitor::CreateApplicationMonitor(
-    ui::EventHandler* event_handler) {
-  return base::WrapUnique(
-      new EventMonitorAura(event_handler, aura::Env::GetInstance()));
+    ui::EventHandler* event_handler,
+    gfx::NativeWindow context) {
+  aura::Env* env = context->env();
+  return std::make_unique<EventMonitorAura>(env, event_handler, env);
 }
 
 // static
 std::unique_ptr<EventMonitor> EventMonitor::CreateWindowMonitor(
     ui::EventHandler* event_handler,
     gfx::NativeWindow target_window) {
-  return base::WrapUnique(new EventMonitorAura(event_handler, target_window));
+  return std::make_unique<EventMonitorAura>(target_window->env(), event_handler,
+                                            target_window);
 }
 
-// static
-gfx::Point EventMonitor::GetLastMouseLocation() {
-  return aura::Env::GetInstance()->last_mouse_location();
-}
-
-EventMonitorAura::EventMonitorAura(ui::EventHandler* event_handler,
+EventMonitorAura::EventMonitorAura(aura::Env* env,
+                                   ui::EventHandler* event_handler,
                                    ui::EventTarget* event_target)
-    : event_handler_(event_handler), event_target_(event_target) {
+    : env_(env), event_handler_(event_handler), event_target_(event_target) {
+  DCHECK(env_);
   DCHECK(event_handler_);
   DCHECK(event_target_);
   event_target_->AddPreTargetHandler(event_handler_);
@@ -43,4 +42,8 @@
   event_target_->RemovePreTargetHandler(event_handler_);
 }
 
+gfx::Point EventMonitorAura::GetLastMouseLocation() {
+  return env_->last_mouse_location();
+}
+
 }  // namespace views
diff --git a/ui/views/event_monitor_aura.h b/ui/views/event_monitor_aura.h
index 64f1c48..68c633b 100644
--- a/ui/views/event_monitor_aura.h
+++ b/ui/views/event_monitor_aura.h
@@ -8,6 +8,10 @@
 #include "base/macros.h"
 #include "ui/views/event_monitor.h"
 
+namespace aura {
+class Env;
+}
+
 namespace ui {
 class EventTarget;
 }
@@ -16,11 +20,16 @@
 
 class EventMonitorAura : public EventMonitor {
  public:
-  EventMonitorAura(ui::EventHandler* event_handler,
+  EventMonitorAura(aura::Env* env,
+                   ui::EventHandler* event_handler,
                    ui::EventTarget* event_target);
   ~EventMonitorAura() override;
 
+  // EventMonitor:
+  gfx::Point GetLastMouseLocation() override;
+
  private:
+  aura::Env* env_;                   // Weak.
   ui::EventHandler* event_handler_;  // Weak. Owned by our owner.
   ui::EventTarget* event_target_;    // Weak.
 
diff --git a/ui/views/event_monitor_mac.h b/ui/views/event_monitor_mac.h
index 3d8fa11b..5cc56a5f 100644
--- a/ui/views/event_monitor_mac.h
+++ b/ui/views/event_monitor_mac.h
@@ -18,6 +18,9 @@
                   gfx::NativeWindow target_window);
   ~EventMonitorMac() override;
 
+  // EventMonitor:
+  gfx::Point GetLastMouseLocation() override;
+
  private:
   id monitor_;
   ui::WeakPtrNSObjectFactory<EventMonitorMac> factory_;
diff --git a/ui/views/event_monitor_mac.mm b/ui/views/event_monitor_mac.mm
index 7485ab26..46562a63 100644
--- a/ui/views/event_monitor_mac.mm
+++ b/ui/views/event_monitor_mac.mm
@@ -17,7 +17,9 @@
 
 // static
 std::unique_ptr<EventMonitor> EventMonitor::CreateApplicationMonitor(
-    ui::EventHandler* event_handler) {
+    ui::EventHandler* event_handler,
+    gfx::NativeWindow context) {
+  // |context| is not needed on Mac.
   return base::WrapUnique(new EventMonitorMac(event_handler, nullptr));
 }
 
@@ -28,11 +30,6 @@
   return base::WrapUnique(new EventMonitorMac(event_handler, target_window));
 }
 
-// static
-gfx::Point EventMonitor::GetLastMouseLocation() {
-  return display::Screen::GetScreen()->GetCursorScreenPoint();
-}
-
 EventMonitorMac::EventMonitorMac(ui::EventHandler* event_handler,
                                  gfx::NativeWindow target_window)
     : factory_(this) {
@@ -67,4 +64,8 @@
   [NSEvent removeMonitor:monitor_];
 }
 
+gfx::Point EventMonitorMac::GetLastMouseLocation() {
+  return display::Screen::GetScreen()->GetCursorScreenPoint();
+}
+
 }  // namespace views
diff --git a/ui/views/event_monitor_unittest.cc b/ui/views/event_monitor_unittest.cc
index fe1f5e0..178ef70b 100644
--- a/ui/views/event_monitor_unittest.cc
+++ b/ui/views/event_monitor_unittest.cc
@@ -46,8 +46,8 @@
 };
 
 TEST_F(EventMonitorTest, ShouldReceiveAppEventsWhileInstalled) {
-  std::unique_ptr<EventMonitor> monitor(
-      EventMonitor::CreateApplicationMonitor(&handler_));
+  std::unique_ptr<EventMonitor> monitor(EventMonitor::CreateApplicationMonitor(
+      &handler_, widget_->GetNativeWindow()));
 
   generator_->ClickLeftButton();
   EXPECT_EQ(2, handler_.num_mouse_events());
@@ -84,8 +84,8 @@
 namespace {
 class DeleteOtherOnEventHandler : public ui::EventHandler {
  public:
-  DeleteOtherOnEventHandler() {
-    monitor_ = EventMonitor::CreateApplicationMonitor(this);
+  explicit DeleteOtherOnEventHandler(gfx::NativeWindow context) {
+    monitor_ = EventMonitor::CreateApplicationMonitor(this, context);
   }
 
   bool DidDelete() const { return !handler_to_delete_; }
@@ -111,16 +111,20 @@
 // Ensure correct behavior when an event monitor is removed while iterating
 // over the OS-controlled observer list.
 TEST_F(EventMonitorTest, TwoMonitors) {
-  auto deleter = std::make_unique<DeleteOtherOnEventHandler>();
-  deleter->set_monitor_to_delete(std::make_unique<DeleteOtherOnEventHandler>());
+  auto deleter =
+      std::make_unique<DeleteOtherOnEventHandler>(widget_->GetNativeWindow());
+  deleter->set_monitor_to_delete(
+      std::make_unique<DeleteOtherOnEventHandler>(widget_->GetNativeWindow()));
 
   EXPECT_FALSE(deleter->DidDelete());
   generator_->PressLeftButton();
   EXPECT_TRUE(deleter->DidDelete());
 
   // Now try setting up observers in the alternate order.
-  auto deletee = std::make_unique<DeleteOtherOnEventHandler>();
-  deleter = std::make_unique<DeleteOtherOnEventHandler>();
+  auto deletee =
+      std::make_unique<DeleteOtherOnEventHandler>(widget_->GetNativeWindow());
+  deleter =
+      std::make_unique<DeleteOtherOnEventHandler>(widget_->GetNativeWindow());
   deleter->set_monitor_to_delete(std::move(deletee));
 
   EXPECT_FALSE(deleter->DidDelete());
diff --git a/ui/views/mouse_watcher.cc b/ui/views/mouse_watcher.cc
index e0c1a3b..5a91062 100644
--- a/ui/views/mouse_watcher.cc
+++ b/ui/views/mouse_watcher.cc
@@ -26,11 +26,10 @@
 
 class MouseWatcher::Observer : public ui::EventHandler {
  public:
-  explicit Observer(MouseWatcher* mouse_watcher)
+  Observer(MouseWatcher* mouse_watcher, gfx::NativeWindow window)
       : mouse_watcher_(mouse_watcher),
-        event_monitor_(EventMonitor::CreateApplicationMonitor(this)),
-        notify_listener_factory_(this) {
-  }
+        event_monitor_(EventMonitor::CreateApplicationMonitor(this, window)),
+        notify_listener_factory_(this) {}
 
   // ui::EventHandler implementation:
   void OnMouseEvent(ui::MouseEvent* event) override {
@@ -57,7 +56,7 @@
   void HandleMouseEvent(MouseWatcherHost::MouseEventType event_type) {
     // It's safe to use last_mouse_location() here as this function is invoked
     // during event dispatching.
-    if (!host()->Contains(EventMonitor::GetLastMouseLocation(), event_type)) {
+    if (!host()->Contains(event_monitor_->GetLastMouseLocation(), event_type)) {
       if (event_type == MouseWatcherHost::MOUSE_PRESS) {
         NotifyListener();
       } else if (!notify_listener_factory_.HasWeakPtrs()) {
@@ -109,9 +108,9 @@
 MouseWatcher::~MouseWatcher() {
 }
 
-void MouseWatcher::Start() {
+void MouseWatcher::Start(gfx::NativeWindow window) {
   if (!is_observing())
-    observer_ = std::make_unique<Observer>(this);
+    observer_ = std::make_unique<Observer>(this, window);
 }
 
 void MouseWatcher::Stop() {
diff --git a/ui/views/mouse_watcher.h b/ui/views/mouse_watcher.h
index c9edf32..d27dc09 100644
--- a/ui/views/mouse_watcher.h
+++ b/ui/views/mouse_watcher.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/views/views_export.h"
 
 namespace gfx {
@@ -65,8 +66,10 @@
   // Starts watching mouse movements. When the mouse moves outside the bounds of
   // the host the listener is notified. |Start| may be invoked any number of
   // times. If the mouse moves outside the bounds of the host the listener is
-  // notified and watcher stops watching events.
-  void Start();
+  // notified and watcher stops watching events. |window| must be a window in
+  // the hierarchy related to the host. |window| is used to setup initial state,
+  // and may be deleted before MouseWatcher.
+  void Start(gfx::NativeWindow window);
 
   // Stops watching mouse events.
   void Stop();
diff --git a/ui/views/widget/desktop_aura/desktop_capture_client.cc b/ui/views/widget/desktop_aura/desktop_capture_client.cc
index c434a9f..86578b9 100644
--- a/ui/views/widget/desktop_aura/desktop_capture_client.cc
+++ b/ui/views/widget/desktop_aura/desktop_capture_client.cc
@@ -5,6 +5,7 @@
 #include "ui/views/widget/desktop_aura/desktop_capture_client.h"
 
 #include "ui/aura/client/capture_client_observer.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tracker.h"
@@ -58,7 +59,8 @@
     // committing |capture_window_|.
     aura::WindowTracker tracker;
     tracker.Add(new_capture_window);
-    ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(new_capture_window);
+    new_capture_window->env()->gesture_recognizer()->CancelActiveTouchesExcept(
+        new_capture_window);
     if (!tracker.Contains(new_capture_window))
       new_capture_window = nullptr;
   }
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index ca16438..4f647af1 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -966,6 +966,10 @@
       desktop_window_tree_host_->IsTranslucentWindowOpacitySupported();
 }
 
+ui::GestureRecognizer* DesktopNativeWidgetAura::GetGestureRecognizer() {
+  return content_window_->env()->gesture_recognizer();
+}
+
 void DesktopNativeWidgetAura::OnSizeConstraintsChanged() {
   int32_t behavior = ui::mojom::kResizeBehaviorNone;
   if (GetWidget()->widget_delegate())
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index da45a84..be95f3a 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -186,6 +186,7 @@
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
   bool IsTranslucentWindowOpacitySupported() const override;
+  ui::GestureRecognizer* GetGestureRecognizer() override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
   std::string GetName() const override;
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index b5a27c9..2d64236 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -799,6 +799,10 @@
   return true;
 }
 
+ui::GestureRecognizer* NativeWidgetAura::GetGestureRecognizer() {
+  return window_->env()->gesture_recognizer();
+}
+
 void NativeWidgetAura::OnSizeConstraintsChanged() {
   if (is_parallel_widget_in_window_manager_)
     return;
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h
index f46dab1..768f0bd 100644
--- a/ui/views/widget/native_widget_aura.h
+++ b/ui/views/widget/native_widget_aura.h
@@ -147,6 +147,7 @@
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
   bool IsTranslucentWindowOpacitySupported() const override;
+  ui::GestureRecognizer* GetGestureRecognizer() override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
   std::string GetName() const override;
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h
index aae36ff..3f2044de 100644
--- a/ui/views/widget/native_widget_mac.h
+++ b/ui/views/widget/native_widget_mac.h
@@ -140,6 +140,7 @@
   void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) override;
   bool IsTranslucentWindowOpacitySupported() const override;
+  ui::GestureRecognizer* GetGestureRecognizer() override;
   void OnSizeConstraintsChanged() override;
   void RepostNativeEvent(gfx::NativeEvent native_event) override;
   std::string GetName() const override;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 53caedb..1a2c2d3a 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/lazy_instance.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
@@ -18,6 +19,7 @@
 #import "ui/base/cocoa/window_size_constants.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/events/gestures/gesture_recognizer_impl_mac.h"
 #include "ui/gfx/font_list.h"
 #import "ui/gfx/mac/coordinate_conversion.h"
 #import "ui/gfx/mac/nswindow_frame_controls.h"
@@ -49,6 +51,9 @@
 namespace views {
 namespace {
 
+base::LazyInstance<ui::GestureRecognizerImplMac>::Leaky
+    g_gesture_recognizer_instance = LAZY_INSTANCE_INITIALIZER;
+
 NSInteger StyleMaskForParams(const Widget::InitParams& params) {
   // If the Widget is modal, it will be displayed as a sheet. This works best if
   // it has NSTitledWindowMask. For example, with NSBorderlessWindowMask, the
@@ -639,6 +644,10 @@
   return false;
 }
 
+ui::GestureRecognizer* NativeWidgetMac::GetGestureRecognizer() {
+  return g_gesture_recognizer_instance.Pointer();
+}
+
 void NativeWidgetMac::OnSizeConstraintsChanged() {
   Widget* widget = GetWidget();
   bridge()->SetSizeConstraints(widget->GetMinimumSize(),
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm
index 48fdedd..ebb9ea6 100644
--- a/ui/views/widget/native_widget_mac_unittest.mm
+++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -1752,7 +1752,7 @@
 };
 
 // Test that undocumented title-hiding API we're using does the job.
-TEST_F(NativeWidgetMacTest, DoesHideTitle) {
+TEST_F(NativeWidgetMacTest, DISABLED_DoesHideTitle) {
   // Same as CreateTopLevelPlatformWidget but with a custom delegate.
   Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
   Widget* widget = new Widget;
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h
index f59ae9f..6aedb90 100644
--- a/ui/views/widget/native_widget_private.h
+++ b/ui/views/widget/native_widget_private.h
@@ -22,6 +22,7 @@
 
 namespace ui {
 class InputMethod;
+class GestureRecognizer;
 class OSExchangeData;
 }
 
@@ -227,6 +228,7 @@
   virtual void SetVisibilityAnimationTransition(
       Widget::VisibilityTransition transition) = 0;
   virtual bool IsTranslucentWindowOpacitySupported() const = 0;
+  virtual ui::GestureRecognizer* GetGestureRecognizer() = 0;
   virtual void OnSizeConstraintsChanged() = 0;
 
   // Repost an unhandled event to the native widget for default OS processing.
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 573adf58..5ad48b6 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -39,6 +39,11 @@
 #include "ui/views/window/custom_frame_view.h"
 #include "ui/views/window/dialog_delegate.h"
 
+#if defined(USE_AURA)
+#include "ui/aura/env.h"     // nogncheck
+#include "ui/aura/window.h"  // nogncheck
+#endif
+
 namespace views {
 
 namespace {
@@ -976,7 +981,12 @@
 
 void Widget::SynthesizeMouseMoveEvent() {
   // In screen coordinate.
-  gfx::Point mouse_location = EventMonitor::GetLastMouseLocation();
+  gfx::Point mouse_location =
+#if defined(USE_AURA)
+      GetNativeWindow()->env()->last_mouse_location();
+#else
+      display::Screen::GetScreen()->GetCursorScreenPoint();
+#endif
   if (!GetWindowBoundsInScreen().Contains(mouse_location))
     return;
 
@@ -993,6 +1003,10 @@
   return native_widget_->IsTranslucentWindowOpacitySupported();
 }
 
+ui::GestureRecognizer* Widget::GetGestureRecognizer() {
+  return native_widget_->GetGestureRecognizer();
+}
+
 void Widget::OnSizeConstraintsChanged() {
   native_widget_->OnSizeConstraintsChanged();
   if (non_client_view_)
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index cbd52e9..2efbe5c 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -49,6 +49,7 @@
 class Accelerator;
 class Compositor;
 class DefaultThemeProvider;
+class GestureRecognizer;
 class InputMethod;
 class Layer;
 class NativeTheme;
@@ -769,6 +770,10 @@
   // Whether the widget supports translucency.
   bool IsTranslucentWindowOpacitySupported() const;
 
+  // Returns the gesture recognizer which can handle touch/gesture events on
+  // this.
+  ui::GestureRecognizer* GetGestureRecognizer();
+
   // Called when the delegate's CanResize or CanMaximize changes.
   void OnSizeConstraintsChanged();
 
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 54b1319..1bd99e8 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1931,7 +1931,8 @@
   widget->SetCapture(view_handler);
 
   ClosingEventHandler monitor_handler(widget);
-  auto monitor = EventMonitor::CreateApplicationMonitor(&monitor_handler);
+  auto monitor = EventMonitor::CreateApplicationMonitor(
+      &monitor_handler, widget->GetNativeWindow());
 
   ui::test::EventGenerator generator(
       IsMus() ? widget->GetNativeWindow() : GetContext(),
diff --git a/ui/wm/core/capture_controller.cc b/ui/wm/core/capture_controller.cc
index fbc045d..e01578e 100644
--- a/ui/wm/core/capture_controller.cc
+++ b/ui/wm/core/capture_controller.cc
@@ -5,6 +5,7 @@
 #include "ui/wm/core/capture_controller.h"
 
 #include "ui/aura/client/capture_client_observer.h"
+#include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tracker.h"
@@ -66,7 +67,8 @@
     // committing |capture_window_|.
     aura::WindowTracker tracker;
     tracker.Add(new_capture_window);
-    ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(new_capture_window);
+    new_capture_window->env()->gesture_recognizer()->CancelActiveTouchesExcept(
+        new_capture_window);
     if (!tracker.Contains(new_capture_window))
       new_capture_window = nullptr;
   }
diff --git a/ui/wm/core/window_modality_controller.cc b/ui/wm/core/window_modality_controller.cc
index 9a80cbc5..847e6cb7 100644
--- a/ui/wm/core/window_modality_controller.cc
+++ b/ui/wm/core/window_modality_controller.cc
@@ -145,7 +145,7 @@
       window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE &&
       window->IsVisible()) {
     ActivateWindow(window);
-    ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
+    env_->gesture_recognizer()->CancelActiveTouchesExcept(nullptr);
   }
 }
 
@@ -154,7 +154,7 @@
     bool visible) {
   if (visible &&
       window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE) {
-    ui::GestureRecognizer::Get()->CancelActiveTouchesExcept(nullptr);
+    env_->gesture_recognizer()->CancelActiveTouchesExcept(nullptr);
     // Make sure no other window has capture, otherwise |window| won't get mouse
     // events.
     aura::Window* capture_window = aura::client::GetCaptureWindow(window);